Commit f0bccc7cfdfd92b35c586be9e6b364a1e20678d1

Authored by Andrew Shvayka
1 parent 6c611694

WIP: Implementation

Showing 32 changed files with 877 additions and 1140 deletions
@@ -31,6 +31,7 @@ import lombok.Setter; @@ -31,6 +31,7 @@ import lombok.Setter;
31 import lombok.extern.slf4j.Slf4j; 31 import lombok.extern.slf4j.Slf4j;
32 import org.springframework.beans.factory.annotation.Autowired; 32 import org.springframework.beans.factory.annotation.Autowired;
33 import org.springframework.beans.factory.annotation.Value; 33 import org.springframework.beans.factory.annotation.Value;
  34 +import org.springframework.context.annotation.Lazy;
34 import org.springframework.stereotype.Component; 35 import org.springframework.stereotype.Component;
35 import org.thingsboard.rule.engine.api.MailService; 36 import org.thingsboard.rule.engine.api.MailService;
36 import org.thingsboard.server.actors.service.ActorService; 37 import org.thingsboard.server.actors.service.ActorService;
@@ -69,6 +70,7 @@ import org.thingsboard.server.service.script.JsExecutorService; @@ -69,6 +70,7 @@ import org.thingsboard.server.service.script.JsExecutorService;
69 import org.thingsboard.server.service.script.JsInvokeService; 70 import org.thingsboard.server.service.script.JsInvokeService;
70 import org.thingsboard.server.service.state.DeviceStateService; 71 import org.thingsboard.server.service.state.DeviceStateService;
71 import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService; 72 import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService;
  73 +import org.thingsboard.server.service.transport.RuleEngineTransportService;
72 74
73 import javax.annotation.Nullable; 75 import javax.annotation.Nullable;
74 import java.io.IOException; 76 import java.io.IOException;
@@ -204,6 +206,11 @@ public class ActorSystemContext { @@ -204,6 +206,11 @@ public class ActorSystemContext {
204 @Getter 206 @Getter
205 private DeviceStateService deviceStateService; 207 private DeviceStateService deviceStateService;
206 208
  209 + @Lazy
  210 + @Autowired
  211 + @Getter
  212 + private RuleEngineTransportService ruleEngineTransportService;
  213 +
207 @Value("${cluster.partition_id}") 214 @Value("${cluster.partition_id}")
208 @Getter 215 @Getter
209 private long queuePartitionId; 216 private long queuePartitionId;
@@ -39,7 +39,6 @@ import org.thingsboard.server.common.msg.aware.TenantAwareMsg; @@ -39,7 +39,6 @@ import org.thingsboard.server.common.msg.aware.TenantAwareMsg;
39 import org.thingsboard.server.common.msg.cluster.SendToClusterMsg; 39 import org.thingsboard.server.common.msg.cluster.SendToClusterMsg;
40 import org.thingsboard.server.common.msg.cluster.ServerAddress; 40 import org.thingsboard.server.common.msg.cluster.ServerAddress;
41 import org.thingsboard.server.common.msg.core.BasicActorSystemToDeviceSessionActorMsg; 41 import org.thingsboard.server.common.msg.core.BasicActorSystemToDeviceSessionActorMsg;
42 -import org.thingsboard.server.common.msg.device.DeviceToDeviceActorMsg;  
43 import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg; 42 import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg;
44 import org.thingsboard.server.common.msg.system.ServiceToRuleEngineMsg; 43 import org.thingsboard.server.common.msg.system.ServiceToRuleEngineMsg;
45 import org.thingsboard.server.dao.model.ModelConstants; 44 import org.thingsboard.server.dao.model.ModelConstants;
@@ -105,7 +104,7 @@ public class AppActor extends RuleChainManagerActor { @@ -105,7 +104,7 @@ public class AppActor extends RuleChainManagerActor {
105 case SERVICE_TO_RULE_ENGINE_MSG: 104 case SERVICE_TO_RULE_ENGINE_MSG:
106 onServiceToRuleEngineMsg((ServiceToRuleEngineMsg) msg); 105 onServiceToRuleEngineMsg((ServiceToRuleEngineMsg) msg);
107 break; 106 break;
108 - case DEVICE_SESSION_TO_DEVICE_ACTOR_MSG: 107 + case TRANSPORT_TO_DEVICE_ACTOR_MSG:
109 case DEVICE_ATTRIBUTES_UPDATE_TO_DEVICE_ACTOR_MSG: 108 case DEVICE_ATTRIBUTES_UPDATE_TO_DEVICE_ACTOR_MSG:
110 case DEVICE_CREDENTIALS_UPDATE_TO_DEVICE_ACTOR_MSG: 109 case DEVICE_CREDENTIALS_UPDATE_TO_DEVICE_ACTOR_MSG:
111 case DEVICE_NAME_OR_TYPE_UPDATE_TO_DEVICE_ACTOR_MSG: 110 case DEVICE_NAME_OR_TYPE_UPDATE_TO_DEVICE_ACTOR_MSG:
@@ -169,16 +168,6 @@ public class AppActor extends RuleChainManagerActor { @@ -169,16 +168,6 @@ public class AppActor extends RuleChainManagerActor {
169 getOrCreateTenantActor(msg.getTenantId()).tell(msg, ActorRef.noSender()); 168 getOrCreateTenantActor(msg.getTenantId()).tell(msg, ActorRef.noSender());
170 } 169 }
171 170
172 - private void processDeviceMsg(DeviceToDeviceActorMsg deviceToDeviceActorMsg) {  
173 - TenantId tenantId = deviceToDeviceActorMsg.getTenantId();  
174 - ActorRef tenantActor = getOrCreateTenantActor(tenantId);  
175 - if (deviceToDeviceActorMsg.getPayload().getMsgType().requiresRulesProcessing()) {  
176 -// tenantActor.tell(new RuleChainDeviceMsg(deviceToDeviceActorMsg, ruleManager.getRuleChain(this.context())), context().self());  
177 - } else {  
178 - tenantActor.tell(deviceToDeviceActorMsg, context().self());  
179 - }  
180 - }  
181 -  
182 private ActorRef getOrCreateTenantActor(TenantId tenantId) { 171 private ActorRef getOrCreateTenantActor(TenantId tenantId) {
183 return tenantActors.computeIfAbsent(tenantId, k -> context().actorOf(Props.create(new TenantActor.ActorCreator(systemContext, tenantId)) 172 return tenantActors.computeIfAbsent(tenantId, k -> context().actorOf(Props.create(new TenantActor.ActorCreator(systemContext, tenantId))
184 .withDispatcher(DefaultActorService.CORE_DISPATCHER_NAME), tenantId.toString())); 173 .withDispatcher(DefaultActorService.CORE_DISPATCHER_NAME), tenantId.toString()));
@@ -26,12 +26,12 @@ import org.thingsboard.server.common.data.id.DeviceId; @@ -26,12 +26,12 @@ import org.thingsboard.server.common.data.id.DeviceId;
26 import org.thingsboard.server.common.data.id.TenantId; 26 import org.thingsboard.server.common.data.id.TenantId;
27 import org.thingsboard.server.common.msg.TbActorMsg; 27 import org.thingsboard.server.common.msg.TbActorMsg;
28 import org.thingsboard.server.common.msg.cluster.ClusterEventMsg; 28 import org.thingsboard.server.common.msg.cluster.ClusterEventMsg;
29 -import org.thingsboard.server.common.msg.device.DeviceToDeviceActorMsg;  
30 import org.thingsboard.server.common.msg.timeout.DeviceActorClientSideRpcTimeoutMsg; 29 import org.thingsboard.server.common.msg.timeout.DeviceActorClientSideRpcTimeoutMsg;
31 import org.thingsboard.server.common.msg.timeout.DeviceActorQueueTimeoutMsg; 30 import org.thingsboard.server.common.msg.timeout.DeviceActorQueueTimeoutMsg;
32 import org.thingsboard.server.common.msg.timeout.DeviceActorServerSideRpcTimeoutMsg; 31 import org.thingsboard.server.common.msg.timeout.DeviceActorServerSideRpcTimeoutMsg;
33 import org.thingsboard.server.service.rpc.ToDeviceRpcRequestActorMsg; 32 import org.thingsboard.server.service.rpc.ToDeviceRpcRequestActorMsg;
34 import org.thingsboard.server.service.rpc.ToServerRpcResponseActorMsg; 33 import org.thingsboard.server.service.rpc.ToServerRpcResponseActorMsg;
  34 +import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper;
35 35
36 public class DeviceActor extends ContextAwareActor { 36 public class DeviceActor extends ContextAwareActor {
37 37
@@ -50,8 +50,8 @@ public class DeviceActor extends ContextAwareActor { @@ -50,8 +50,8 @@ public class DeviceActor extends ContextAwareActor {
50 case CLUSTER_EVENT_MSG: 50 case CLUSTER_EVENT_MSG:
51 processor.processClusterEventMsg((ClusterEventMsg) msg); 51 processor.processClusterEventMsg((ClusterEventMsg) msg);
52 break; 52 break;
53 - case DEVICE_SESSION_TO_DEVICE_ACTOR_MSG:  
54 - processor.process(context(), (DeviceToDeviceActorMsg) msg); 53 + case TRANSPORT_TO_DEVICE_ACTOR_MSG:
  54 + processor.process(context(), (TransportToDeviceActorMsgWrapper) msg);
55 break; 55 break;
56 case DEVICE_ATTRIBUTES_UPDATE_TO_DEVICE_ACTOR_MSG: 56 case DEVICE_ATTRIBUTES_UPDATE_TO_DEVICE_ACTOR_MSG:
57 processor.processAttributesUpdate(context(), (DeviceAttributesEventNotificationMsg) msg); 57 processor.processAttributesUpdate(context(), (DeviceAttributesEventNotificationMsg) msg);
1 /** 1 /**
2 * Copyright © 2016-2018 The Thingsboard Authors 2 * Copyright © 2016-2018 The Thingsboard Authors
3 - * 3 + * <p>
4 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License. 5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at 6 * You may obtain a copy of the License at
7 - *  
8 - * http://www.apache.org/licenses/LICENSE-2.0  
9 - * 7 + * <p>
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + * <p>
10 * Unless required by applicable law or agreed to in writing, software 10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS, 11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -61,7 +61,6 @@ import org.thingsboard.server.common.msg.core.TelemetryUploadRequest; @@ -61,7 +61,6 @@ import org.thingsboard.server.common.msg.core.TelemetryUploadRequest;
61 import org.thingsboard.server.common.msg.core.ToDeviceRpcRequestMsg; 61 import org.thingsboard.server.common.msg.core.ToDeviceRpcRequestMsg;
62 import org.thingsboard.server.common.msg.core.ToDeviceRpcResponseMsg; 62 import org.thingsboard.server.common.msg.core.ToDeviceRpcResponseMsg;
63 import org.thingsboard.server.common.msg.core.ToServerRpcRequestMsg; 63 import org.thingsboard.server.common.msg.core.ToServerRpcRequestMsg;
64 -import org.thingsboard.server.common.msg.device.DeviceToDeviceActorMsg;  
65 import org.thingsboard.server.common.msg.kv.BasicAttributeKVMsg; 64 import org.thingsboard.server.common.msg.kv.BasicAttributeKVMsg;
66 import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest; 65 import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest;
67 import org.thingsboard.server.common.msg.session.FromDeviceMsg; 66 import org.thingsboard.server.common.msg.session.FromDeviceMsg;
@@ -71,9 +70,11 @@ import org.thingsboard.server.common.msg.session.ToDeviceMsg; @@ -71,9 +70,11 @@ import org.thingsboard.server.common.msg.session.ToDeviceMsg;
71 import org.thingsboard.server.common.msg.timeout.DeviceActorClientSideRpcTimeoutMsg; 70 import org.thingsboard.server.common.msg.timeout.DeviceActorClientSideRpcTimeoutMsg;
72 import org.thingsboard.server.common.msg.timeout.DeviceActorQueueTimeoutMsg; 71 import org.thingsboard.server.common.msg.timeout.DeviceActorQueueTimeoutMsg;
73 import org.thingsboard.server.common.msg.timeout.DeviceActorServerSideRpcTimeoutMsg; 72 import org.thingsboard.server.common.msg.timeout.DeviceActorServerSideRpcTimeoutMsg;
  73 +import org.thingsboard.server.gen.transport.TransportProtos;
74 import org.thingsboard.server.service.rpc.FromDeviceRpcResponse; 74 import org.thingsboard.server.service.rpc.FromDeviceRpcResponse;
75 import org.thingsboard.server.service.rpc.ToDeviceRpcRequestActorMsg; 75 import org.thingsboard.server.service.rpc.ToDeviceRpcRequestActorMsg;
76 import org.thingsboard.server.service.rpc.ToServerRpcResponseActorMsg; 76 import org.thingsboard.server.service.rpc.ToServerRpcResponseActorMsg;
  77 +import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper;
77 78
78 import javax.annotation.Nullable; 79 import javax.annotation.Nullable;
79 import java.util.ArrayList; 80 import java.util.ArrayList;
@@ -92,6 +93,8 @@ import java.util.function.Consumer; @@ -92,6 +93,8 @@ import java.util.function.Consumer;
92 import java.util.function.Predicate; 93 import java.util.function.Predicate;
93 import java.util.stream.Collectors; 94 import java.util.stream.Collectors;
94 95
  96 +import org.thingsboard.server.gen.transport.TransportProtos.*;
  97 +
95 /** 98 /**
96 * @author Andrew Shvayka 99 * @author Andrew Shvayka
97 */ 100 */
@@ -99,12 +102,11 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso @@ -99,12 +102,11 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
99 102
100 private final TenantId tenantId; 103 private final TenantId tenantId;
101 private final DeviceId deviceId; 104 private final DeviceId deviceId;
102 - private final Map<SessionId, SessionInfo> sessions;  
103 - private final Map<SessionId, SessionInfo> attributeSubscriptions;  
104 - private final Map<SessionId, SessionInfo> rpcSubscriptions; 105 + private final Map<UUID, SessionInfo> sessions;
  106 + private final Map<UUID, SessionInfo> attributeSubscriptions;
  107 + private final Map<UUID, SessionInfo> rpcSubscriptions;
105 private final Map<Integer, ToDeviceRpcRequestMetadata> toDeviceRpcPendingMap; 108 private final Map<Integer, ToDeviceRpcRequestMetadata> toDeviceRpcPendingMap;
106 private final Map<Integer, ToServerRpcRequestMetadata> toServerRpcPendingMap; 109 private final Map<Integer, ToServerRpcRequestMetadata> toServerRpcPendingMap;
107 - private final Map<UUID, PendingSessionMsgData> pendingMsgs;  
108 110
109 private final Gson gson = new Gson(); 111 private final Gson gson = new Gson();
110 private final JsonParser jsonParser = new JsonParser(); 112 private final JsonParser jsonParser = new JsonParser();
@@ -123,7 +125,6 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso @@ -123,7 +125,6 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
123 this.rpcSubscriptions = new HashMap<>(); 125 this.rpcSubscriptions = new HashMap<>();
124 this.toDeviceRpcPendingMap = new HashMap<>(); 126 this.toDeviceRpcPendingMap = new HashMap<>();
125 this.toServerRpcPendingMap = new HashMap<>(); 127 this.toServerRpcPendingMap = new HashMap<>();
126 - this.pendingMsgs = new HashMap<>();  
127 initAttributes(); 128 initAttributes();
128 } 129 }
129 130
@@ -154,11 +155,11 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso @@ -154,11 +155,11 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
154 boolean sent = rpcSubscriptions.size() > 0; 155 boolean sent = rpcSubscriptions.size() > 0;
155 Set<SessionId> syncSessionSet = new HashSet<>(); 156 Set<SessionId> syncSessionSet = new HashSet<>();
156 rpcSubscriptions.entrySet().forEach(sub -> { 157 rpcSubscriptions.entrySet().forEach(sub -> {
157 - ActorSystemToDeviceSessionActorMsg response = new BasicActorSystemToDeviceSessionActorMsg(rpcRequest, sub.getKey());  
158 - sendMsgToSessionActor(response, sub.getValue().getServer());  
159 - if (SessionType.SYNC == sub.getValue().getType()) {  
160 - syncSessionSet.add(sub.getKey());  
161 - } 158 +// ActorSystemToDeviceSessionActorMsg response = new BasicActorSystemToDeviceSessionActorMsg(rpcRequest, sub.getKey());
  159 +// sendMsgToSessionActor(response, sub.getValue().getServer());
  160 +// if (SessionType.SYNC == sub.getValue().getType()) {
  161 +// syncSessionSet.add(sub.getKey());
  162 +// }
162 }); 163 });
163 syncSessionSet.forEach(rpcSubscriptions::remove); 164 syncSessionSet.forEach(rpcSubscriptions::remove);
164 165
@@ -191,15 +192,6 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso @@ -191,15 +192,6 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
191 } 192 }
192 } 193 }
193 194
194 - void processQueueTimeout(ActorContext context, DeviceActorQueueTimeoutMsg msg) {  
195 - PendingSessionMsgData data = pendingMsgs.remove(msg.getId());  
196 - if (data != null) {  
197 - logger.debug("[{}] Queue put [{}] timeout detected!", deviceId, msg.getId());  
198 - ToDeviceMsg toDeviceMsg = new RuleEngineErrorMsg(data.getSessionMsgType(), RuleEngineError.QUEUE_PUT_TIMEOUT);  
199 - sendMsgToSessionActor(new BasicActorSystemToDeviceSessionActorMsg(toDeviceMsg, data.getSessionId()), data.getServerAddress());  
200 - }  
201 - }  
202 -  
203 void processQueueAck(ActorContext context, RuleEngineQueuePutAckMsg msg) { 195 void processQueueAck(ActorContext context, RuleEngineQueuePutAckMsg msg) {
204 PendingSessionMsgData data = pendingMsgs.remove(msg.getId()); 196 PendingSessionMsgData data = pendingMsgs.remove(msg.getId());
205 if (data != null && data.isReplyOnQueueAck()) { 197 if (data != null && data.isReplyOnQueueAck()) {
@@ -252,31 +244,37 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso @@ -252,31 +244,37 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
252 }; 244 };
253 } 245 }
254 246
255 - void process(ActorContext context, DeviceToDeviceActorMsg msg) {  
256 - processSubscriptionCommands(context, msg);  
257 - processRpcResponses(context, msg);  
258 - processSessionStateMsgs(msg); 247 + void process(ActorContext context, TransportToDeviceActorMsgWrapper wrapper) {
  248 + TransportToDeviceActorMsg msg = wrapper.getMsg();
  249 +// processSubscriptionCommands(context, msg);
  250 +// processRpcResponses(context, msg);
  251 + if (msg.hasSessionEvent()) {
  252 + processSessionStateMsgs(msg.getSessionInfo(), msg.getSessionEvent());
  253 + }
259 254
260 - SessionMsgType sessionMsgType = msg.getPayload().getMsgType();  
261 - if (sessionMsgType.requiresRulesProcessing()) {  
262 - switch (sessionMsgType) {  
263 - case GET_ATTRIBUTES_REQUEST:  
264 - handleGetAttributesRequest(msg);  
265 - break;  
266 - case POST_ATTRIBUTES_REQUEST:  
267 - handlePostAttributesRequest(context, msg);  
268 - reportActivity();  
269 - break;  
270 - case POST_TELEMETRY_REQUEST:  
271 - handlePostTelemetryRequest(context, msg);  
272 - reportActivity();  
273 - break;  
274 - case TO_SERVER_RPC_REQUEST:  
275 - handleClientSideRPCRequest(context, msg);  
276 - reportActivity();  
277 - break;  
278 - } 255 + if (msg.hasPostAttributes()) {
  256 + handlePostAttributesRequest(context, msg.getSessionInfo(), msg.getPostAttributes());
  257 + reportActivity();
279 } 258 }
  259 + if (msg.hasPostTelemetry()) {
  260 + handlePostTelemetryRequest(context, msg.getSessionInfo(), msg.getPostTelemetry());
  261 + reportActivity();
  262 + }
  263 + if (msg.hasGetAttributes()) {
  264 + handleGetAttributesRequest(context, msg.getSessionInfo(), msg.getGetAttributes());
  265 + }
  266 +// SessionMsgType sessionMsgType = msg.getPayload().getMsgType();
  267 +// if (sessionMsgType.requiresRulesProcessing()) {
  268 +// switch (sessionMsgType) {
  269 +// case GET_ATTRIBUTES_REQUEST:
  270 +// handleGetAttributesRequest(msg);
  271 +// break;
  272 +// case TO_SERVER_RPC_REQUEST:
  273 +// handleClientSideRPCRequest(context, msg);
  274 +// reportActivity();
  275 +// break;
  276 +// }
  277 +// }
280 } 278 }
281 279
282 private void reportActivity() { 280 private void reportActivity() {
@@ -291,6 +289,39 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso @@ -291,6 +289,39 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
291 systemContext.getDeviceStateService().onDeviceDisconnect(deviceId); 289 systemContext.getDeviceStateService().onDeviceDisconnect(deviceId);
292 } 290 }
293 291
  292 + private void handleGetAttributesRequest(ActorContext context, SessionInfoProto sessionInfo, GetAttributeRequestMsg request) {
  293 + ListenableFuture<List<AttributeKvEntry>> clientAttributesFuture = getAttributeKvEntries(deviceId, DataConstants.CLIENT_SCOPE, toOptionalSet(request.getClientAttributeNamesList()));
  294 + ListenableFuture<List<AttributeKvEntry>> sharedAttributesFuture = getAttributeKvEntries(deviceId, DataConstants.SHARED_SCOPE, toOptionalSet(request.getSharedAttributeNamesList()));
  295 +
  296 + Futures.addCallback(Futures.allAsList(Arrays.asList(clientAttributesFuture, sharedAttributesFuture)), new FutureCallback<List<List<AttributeKvEntry>>>() {
  297 + @Override
  298 + public void onSuccess(@Nullable List<List<AttributeKvEntry>> result) {
  299 + systemContext.getRuleEngineTransportService().process();
  300 + BasicGetAttributesResponse response = BasicGetAttributesResponse.onSuccess(request.getMsgType(),
  301 + request.getRequestId(), BasicAttributeKVMsg.from(result.get(0), result.get(1)));
  302 + sendMsgToSessionActor(new BasicActorSystemToDeviceSessionActorMsg(response, src.getSessionId()), src.getServerAddress());
  303 + }
  304 +
  305 + @Override
  306 + public void onFailure(Throwable t) {
  307 + if (t instanceof Exception) {
  308 + ToDeviceMsg toDeviceMsg = BasicStatusCodeResponse.onError(SessionMsgType.GET_ATTRIBUTES_REQUEST, request.getRequestId(), (Exception) t);
  309 + sendMsgToSessionActor(new BasicActorSystemToDeviceSessionActorMsg(toDeviceMsg, src.getSessionId()), src.getServerAddress());
  310 + } else {
  311 + logger.error("[{}] Failed to process attributes request", deviceId, t);
  312 + }
  313 + }
  314 + });
  315 + }
  316 +
  317 + private Optional<Set<String>> toOptionalSet(List<String> strings) {
  318 + if (strings == null || strings.isEmpty()) {
  319 + return Optional.empty();
  320 + } else {
  321 + return Optional.of(new HashSet<>(strings));
  322 + }
  323 + }
  324 +
294 private void handleGetAttributesRequest(DeviceToDeviceActorMsg src) { 325 private void handleGetAttributesRequest(DeviceToDeviceActorMsg src) {
295 GetAttributesRequest request = (GetAttributesRequest) src.getPayload(); 326 GetAttributesRequest request = (GetAttributesRequest) src.getPayload();
296 ListenableFuture<List<AttributeKvEntry>> clientAttributesFuture = getAttributeKvEntries(deviceId, DataConstants.CLIENT_SCOPE, request.getClientAttributeNames()); 327 ListenableFuture<List<AttributeKvEntry>> clientAttributesFuture = getAttributeKvEntries(deviceId, DataConstants.CLIENT_SCOPE, request.getClientAttributeNames());
@@ -328,43 +359,20 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso @@ -328,43 +359,20 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
328 } 359 }
329 } 360 }
330 361
331 - private void handlePostAttributesRequest(ActorContext context, DeviceToDeviceActorMsg src) {  
332 - AttributesUpdateRequest request = (AttributesUpdateRequest) src.getPayload();  
333 -  
334 - JsonObject json = new JsonObject();  
335 - for (AttributeKvEntry kv : request.getAttributes()) {  
336 - kv.getBooleanValue().ifPresent(v -> json.addProperty(kv.getKey(), v));  
337 - kv.getLongValue().ifPresent(v -> json.addProperty(kv.getKey(), v));  
338 - kv.getDoubleValue().ifPresent(v -> json.addProperty(kv.getKey(), v));  
339 - kv.getStrValue().ifPresent(v -> json.addProperty(kv.getKey(), v));  
340 - }  
341 -  
342 - TbMsg tbMsg = new TbMsg(UUIDs.timeBased(), SessionMsgType.POST_ATTRIBUTES_REQUEST.name(), deviceId, defaultMetaData.copy(), TbMsgDataType.JSON, gson.toJson(json), null, null, 0L);  
343 - PendingSessionMsgData msgData = new PendingSessionMsgData(src.getSessionId(), src.getServerAddress(),  
344 - SessionMsgType.POST_ATTRIBUTES_REQUEST, request.getRequestId(), true, 1);  
345 - pushToRuleEngineWithTimeout(context, tbMsg, msgData); 362 + private void handlePostAttributesRequest(ActorContext context, SessionInfoProto sessionInfo, PostAttributeMsg postAttributes) {
  363 + JsonObject json = getJsonObject(postAttributes.getKvList());
  364 + TbMsg tbMsg = new TbMsg(UUIDs.timeBased(), SessionMsgType.POST_ATTRIBUTES_REQUEST.name(), deviceId, defaultMetaData.copy(),
  365 + TbMsgDataType.JSON, gson.toJson(json), null, null, 0L);
  366 + pushToRuleEngine(context, tbMsg);
346 } 367 }
347 368
348 - private void handlePostTelemetryRequest(ActorContext context, DeviceToDeviceActorMsg src) {  
349 - TelemetryUploadRequest request = (TelemetryUploadRequest) src.getPayload();  
350 -  
351 - Map<Long, List<KvEntry>> tsData = request.getData();  
352 -  
353 - PendingSessionMsgData msgData = new PendingSessionMsgData(src.getSessionId(), src.getServerAddress(),  
354 - SessionMsgType.POST_TELEMETRY_REQUEST, request.getRequestId(), true, tsData.size());  
355 -  
356 - for (Map.Entry<Long, List<KvEntry>> entry : tsData.entrySet()) {  
357 - JsonObject json = new JsonObject();  
358 - for (KvEntry kv : entry.getValue()) {  
359 - kv.getBooleanValue().ifPresent(v -> json.addProperty(kv.getKey(), v));  
360 - kv.getLongValue().ifPresent(v -> json.addProperty(kv.getKey(), v));  
361 - kv.getDoubleValue().ifPresent(v -> json.addProperty(kv.getKey(), v));  
362 - kv.getStrValue().ifPresent(v -> json.addProperty(kv.getKey(), v));  
363 - } 369 + private void handlePostTelemetryRequest(ActorContext context, SessionInfoProto sessionInfo, PostTelemetryMsg postTelemetry) {
  370 + for (TsKvListProto tsKv : postTelemetry.getTsKvListList()) {
  371 + JsonObject json = getJsonObject(tsKv.getKvList());
364 TbMsgMetaData metaData = defaultMetaData.copy(); 372 TbMsgMetaData metaData = defaultMetaData.copy();
365 - metaData.putValue("ts", entry.getKey() + ""); 373 + metaData.putValue("ts", tsKv.getTs() + "");
366 TbMsg tbMsg = new TbMsg(UUIDs.timeBased(), SessionMsgType.POST_TELEMETRY_REQUEST.name(), deviceId, metaData, TbMsgDataType.JSON, gson.toJson(json), null, null, 0L); 374 TbMsg tbMsg = new TbMsg(UUIDs.timeBased(), SessionMsgType.POST_TELEMETRY_REQUEST.name(), deviceId, metaData, TbMsgDataType.JSON, gson.toJson(json), null, null, 0L);
367 - pushToRuleEngineWithTimeout(context, tbMsg, msgData); 375 + pushToRuleEngine(context, tbMsg);
368 } 376 }
369 } 377 }
370 378
@@ -401,16 +409,7 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso @@ -401,16 +409,7 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
401 } 409 }
402 } 410 }
403 411
404 - private void pushToRuleEngineWithTimeout(ActorContext context, TbMsg tbMsg, PendingSessionMsgData pendingMsgData) {  
405 - SessionMsgType sessionMsgType = pendingMsgData.getSessionMsgType();  
406 - int requestId = pendingMsgData.getRequestId();  
407 - if (systemContext.isQueuePersistenceEnabled()) {  
408 - pendingMsgs.put(tbMsg.getId(), pendingMsgData);  
409 - scheduleMsgWithDelay(context, new DeviceActorQueueTimeoutMsg(tbMsg.getId(), systemContext.getQueuePersistenceTimeout()), systemContext.getQueuePersistenceTimeout());  
410 - } else {  
411 - ActorSystemToDeviceSessionActorMsg response = new BasicActorSystemToDeviceSessionActorMsg(BasicStatusCodeResponse.onSuccess(sessionMsgType, requestId), pendingMsgData.getSessionId());  
412 - sendMsgToSessionActor(response, pendingMsgData.getServerAddress());  
413 - } 412 + private void pushToRuleEngine(ActorContext context, TbMsg tbMsg) {
414 context.parent().tell(new DeviceActorToRuleEngineMsg(context.self(), tbMsg), context.self()); 413 context.parent().tell(new DeviceActorToRuleEngineMsg(context.self(), tbMsg), context.self());
415 } 414 }
416 415
@@ -497,13 +496,12 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso @@ -497,13 +496,12 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
497 } 496 }
498 } 497 }
499 498
500 - private void processSessionStateMsgs(DeviceToDeviceActorMsg msg) {  
501 - SessionId sessionId = msg.getSessionId();  
502 - FromDeviceMsg inMsg = msg.getPayload();  
503 - if (inMsg instanceof SessionOpenMsg) { 499 + private void processSessionStateMsgs(SessionInfoProto sessionInfo, SessionEventMsg msg) {
  500 + UUID sessionId = new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB());
  501 + if (msg.getEvent() == SessionEvent.OPEN) {
504 logger.debug("[{}] Processing new session [{}]", deviceId, sessionId); 502 logger.debug("[{}] Processing new session [{}]", deviceId, sessionId);
505 if (sessions.size() >= systemContext.getMaxConcurrentSessionsPerDevice()) { 503 if (sessions.size() >= systemContext.getMaxConcurrentSessionsPerDevice()) {
506 - SessionId sessionIdToRemove = sessions.keySet().stream().findFirst().orElse(null); 504 + UUID sessionIdToRemove = sessions.keySet().stream().findFirst().orElse(null);
507 if (sessionIdToRemove != null) { 505 if (sessionIdToRemove != null) {
508 closeSession(sessionIdToRemove, sessions.remove(sessionIdToRemove)); 506 closeSession(sessionIdToRemove, sessions.remove(sessionIdToRemove));
509 } 507 }
@@ -512,6 +510,10 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso @@ -512,6 +510,10 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
512 if (sessions.size() == 1) { 510 if (sessions.size() == 1) {
513 reportSessionOpen(); 511 reportSessionOpen();
514 } 512 }
  513 + }
  514 + FromDeviceMsg inMsg = msg.getPayload();
  515 + if (inMsg instanceof SessionOpenMsg) {
  516 + logger.debug("[{}] Processing new session [{}]", deviceId, sessionId);
515 } else if (inMsg instanceof SessionCloseMsg) { 517 } else if (inMsg instanceof SessionCloseMsg) {
516 logger.debug("[{}] Canceling subscriptions for closed session [{}]", deviceId, sessionId); 518 logger.debug("[{}] Canceling subscriptions for closed session [{}]", deviceId, sessionId);
517 sessions.remove(sessionId); 519 sessions.remove(sessionId);
@@ -540,8 +542,12 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso @@ -540,8 +542,12 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
540 rpcSubscriptions.clear(); 542 rpcSubscriptions.clear();
541 } 543 }
542 544
543 - private void closeSession(SessionId sessionId, SessionInfo sessionInfo) {  
544 - sendMsgToSessionActor(new BasicActorSystemToDeviceSessionActorMsg(new SessionCloseNotification(), sessionId), sessionInfo.getServer()); 545 + private void closeSession(UUID sessionId, SessionInfo sessionInfo) {
  546 + DeviceActorToTransportMsg msg = DeviceActorToTransportMsg.newBuilder()
  547 + .setSessionIdMSB(sessionId.getMostSignificantBits())
  548 + .setSessionIdLSB(sessionId.getLeastSignificantBits())
  549 + .setSessionCloseNotification(SessionCloseNotificationProto.getDefaultInstance()).build();
  550 + systemContext.getRuleEngineTransportService().process(sessionInfo.getNodeId(), msg);
545 } 551 }
546 552
547 void processNameOrTypeUpdate(DeviceNameOrTypeUpdateMsg msg) { 553 void processNameOrTypeUpdate(DeviceNameOrTypeUpdateMsg msg) {
@@ -552,4 +558,24 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso @@ -552,4 +558,24 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
552 this.defaultMetaData.putValue("deviceType", deviceType); 558 this.defaultMetaData.putValue("deviceType", deviceType);
553 } 559 }
554 560
  561 + private JsonObject getJsonObject(List<KeyValueProto> tsKv) {
  562 + JsonObject json = new JsonObject();
  563 + for (KeyValueProto kv : tsKv) {
  564 + switch (kv.getType()) {
  565 + case BOOLEAN_V:
  566 + json.addProperty(kv.getKey(), kv.getBoolV());
  567 + break;
  568 + case LONG_V:
  569 + json.addProperty(kv.getKey(), kv.getLongV());
  570 + break;
  571 + case DOUBLE_V:
  572 + json.addProperty(kv.getKey(), kv.getDoubleV());
  573 + break;
  574 + case STRING_V:
  575 + json.addProperty(kv.getKey(), kv.getStringV());
  576 + break;
  577 + }
  578 + }
  579 + return json;
  580 + }
555 } 581 }
@@ -22,6 +22,7 @@ import org.thingsboard.server.common.msg.cluster.ServerAddress; @@ -22,6 +22,7 @@ import org.thingsboard.server.common.msg.cluster.ServerAddress;
22 import org.thingsboard.server.common.msg.session.SessionMsgType; 22 import org.thingsboard.server.common.msg.session.SessionMsgType;
23 23
24 import java.util.Optional; 24 import java.util.Optional;
  25 +import java.util.UUID;
25 26
26 /** 27 /**
27 * Created by ashvayka on 17.04.18. 28 * Created by ashvayka on 17.04.18.
@@ -30,7 +31,7 @@ import java.util.Optional; @@ -30,7 +31,7 @@ import java.util.Optional;
30 @AllArgsConstructor 31 @AllArgsConstructor
31 public final class PendingSessionMsgData { 32 public final class PendingSessionMsgData {
32 33
33 - private final SessionId sessionId; 34 + private final UUID sessionId;
34 private final Optional<ServerAddress> serverAddress; 35 private final Optional<ServerAddress> serverAddress;
35 private final SessionMsgType sessionMsgType; 36 private final SessionMsgType sessionMsgType;
36 private final int requestId; 37 private final int requestId;
1 /** 1 /**
2 * Copyright © 2016-2018 The Thingsboard Authors 2 * Copyright © 2016-2018 The Thingsboard Authors
3 - * 3 + * <p>
4 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License. 5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at 6 * You may obtain a copy of the License at
7 - *  
8 - * http://www.apache.org/licenses/LICENSE-2.0  
9 - * 7 + * <p>
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + * <p>
10 * Unless required by applicable law or agreed to in writing, software 10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS, 11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -16,10 +16,7 @@ @@ -16,10 +16,7 @@
16 package org.thingsboard.server.actors.device; 16 package org.thingsboard.server.actors.device;
17 17
18 import lombok.Data; 18 import lombok.Data;
19 -import org.thingsboard.server.common.msg.cluster.ServerAddress;  
20 -import org.thingsboard.server.common.msg.session.SessionType;  
21 -  
22 -import java.util.Optional; 19 +import org.thingsboard.server.gen.transport.TransportProtos.SessionType;
23 20
24 /** 21 /**
25 * @author Andrew Shvayka 22 * @author Andrew Shvayka
@@ -27,5 +24,6 @@ import java.util.Optional; @@ -27,5 +24,6 @@ import java.util.Optional;
27 @Data 24 @Data
28 public class SessionInfo { 25 public class SessionInfo {
29 private final SessionType type; 26 private final SessionType type;
30 - private final Optional<ServerAddress> server; 27 + private final String nodeId;
  28 +
31 } 29 }
1 -/**  
2 - * Copyright © 2016-2018 The Thingsboard Authors  
3 - *  
4 - * Licensed under the Apache License, Version 2.0 (the "License");  
5 - * you may not use this file except in compliance with the License.  
6 - * You may obtain a copy of the License at  
7 - *  
8 - * http://www.apache.org/licenses/LICENSE-2.0  
9 - *  
10 - * Unless required by applicable law or agreed to in writing, software  
11 - * distributed under the License is distributed on an "AS IS" BASIS,  
12 - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  
13 - * See the License for the specific language governing permissions and  
14 - * limitations under the License.  
15 - */  
16 -package org.thingsboard.server.actors.session;  
17 -  
18 -import akka.actor.ActorContext;  
19 -import akka.event.LoggingAdapter;  
20 -import org.thingsboard.server.actors.ActorSystemContext;  
21 -import org.thingsboard.server.actors.shared.SessionTimeoutMsg;  
22 -import org.thingsboard.server.common.data.id.SessionId;  
23 -import org.thingsboard.server.common.msg.cluster.ClusterEventMsg;  
24 -import org.thingsboard.server.common.msg.cluster.ServerAddress;  
25 -import org.thingsboard.server.common.msg.core.AttributesSubscribeMsg;  
26 -import org.thingsboard.server.common.msg.core.ResponseMsg;  
27 -import org.thingsboard.server.common.msg.core.RpcSubscribeMsg;  
28 -import org.thingsboard.server.common.msg.core.SessionCloseMsg;  
29 -import org.thingsboard.server.common.msg.core.SessionOpenMsg;  
30 -import org.thingsboard.server.common.msg.device.DeviceToDeviceActorMsg;  
31 -import org.thingsboard.server.common.msg.session.BasicSessionActorToAdaptorMsg;  
32 -import org.thingsboard.server.common.msg.session.FromDeviceMsg;  
33 -import org.thingsboard.server.common.msg.session.FromDeviceRequestMsg;  
34 -import org.thingsboard.server.common.msg.session.SessionMsgType;  
35 -import org.thingsboard.server.common.msg.session.SessionType;  
36 -import org.thingsboard.server.common.msg.session.ToDeviceMsg;  
37 -import org.thingsboard.server.common.msg.session.TransportToDeviceSessionActorMsg;  
38 -import org.thingsboard.server.common.msg.session.ex.SessionException;  
39 -  
40 -import java.util.HashMap;  
41 -import java.util.Map;  
42 -import java.util.Optional;  
43 -  
44 -class ASyncMsgProcessor extends AbstractSessionActorMsgProcessor {  
45 -  
46 - private boolean firstMsg = true;  
47 - private Map<Integer, DeviceToDeviceActorMsg> pendingMap = new HashMap<>();  
48 - private Optional<ServerAddress> currentTargetServer;  
49 - private boolean subscribedToAttributeUpdates;  
50 - private boolean subscribedToRpcCommands;  
51 -  
52 - public ASyncMsgProcessor(ActorSystemContext ctx, LoggingAdapter logger, SessionId sessionId) {  
53 - super(ctx, logger, sessionId);  
54 - }  
55 -  
56 - @Override  
57 - protected void processToDeviceActorMsg(ActorContext ctx, TransportToDeviceSessionActorMsg msg) {  
58 - updateSessionCtx(msg, SessionType.ASYNC);  
59 - DeviceToDeviceActorMsg pendingMsg = toDeviceMsg(msg);  
60 - FromDeviceMsg fromDeviceMsg = pendingMsg.getPayload();  
61 - if (firstMsg) {  
62 - if (fromDeviceMsg.getMsgType() != SessionMsgType.SESSION_OPEN) {  
63 - toDeviceMsg(new SessionOpenMsg()).ifPresent(m -> forwardToAppActor(ctx, m));  
64 - }  
65 - firstMsg = false;  
66 - }  
67 - switch (fromDeviceMsg.getMsgType()) {  
68 - case POST_TELEMETRY_REQUEST:  
69 - case POST_ATTRIBUTES_REQUEST:  
70 - FromDeviceRequestMsg requestMsg = (FromDeviceRequestMsg) fromDeviceMsg;  
71 - if (requestMsg.getRequestId() >= 0) {  
72 - logger.debug("[{}] Pending request {} registered", requestMsg.getRequestId(), requestMsg.getMsgType());  
73 - //TODO: handle duplicates.  
74 - pendingMap.put(requestMsg.getRequestId(), pendingMsg);  
75 - }  
76 - break;  
77 - case SUBSCRIBE_ATTRIBUTES_REQUEST:  
78 - subscribedToAttributeUpdates = true;  
79 - break;  
80 - case UNSUBSCRIBE_ATTRIBUTES_REQUEST:  
81 - subscribedToAttributeUpdates = false;  
82 - break;  
83 - case SUBSCRIBE_RPC_COMMANDS_REQUEST:  
84 - subscribedToRpcCommands = true;  
85 - break;  
86 - case UNSUBSCRIBE_RPC_COMMANDS_REQUEST:  
87 - subscribedToRpcCommands = false;  
88 - break;  
89 - default:  
90 - break;  
91 - }  
92 - currentTargetServer = forwardToAppActor(ctx, pendingMsg);  
93 - }  
94 -  
95 - @Override  
96 - public void processToDeviceMsg(ActorContext context, ToDeviceMsg msg) {  
97 - try {  
98 - if (msg.getSessionMsgType() != SessionMsgType.SESSION_CLOSE) {  
99 - switch (msg.getSessionMsgType()) {  
100 - case STATUS_CODE_RESPONSE:  
101 - case GET_ATTRIBUTES_RESPONSE:  
102 - ResponseMsg responseMsg = (ResponseMsg) msg;  
103 - if (responseMsg.getRequestId() >= 0) {  
104 - logger.debug("[{}] Pending request processed: {}", responseMsg.getRequestId(), responseMsg);  
105 - pendingMap.remove(responseMsg.getRequestId());  
106 - }  
107 - break;  
108 - default:  
109 - break;  
110 - }  
111 - sessionCtx.onMsg(new BasicSessionActorToAdaptorMsg(this.sessionCtx, msg));  
112 - } else {  
113 - sessionCtx.onMsg(org.thingsboard.server.common.msg.session.ctrl.SessionCloseMsg.onCredentialsRevoked(sessionCtx.getSessionId()));  
114 - }  
115 - } catch (SessionException e) {  
116 - logger.warning("Failed to push session response msg", e);  
117 - }  
118 - }  
119 -  
120 - @Override  
121 - public void processTimeoutMsg(ActorContext context, SessionTimeoutMsg msg) {  
122 - // TODO Auto-generated method stub  
123 - }  
124 -  
125 - @Override  
126 - protected void cleanupSession(ActorContext ctx) {  
127 - toDeviceMsg(new SessionCloseMsg()).ifPresent(m -> forwardToAppActor(ctx, m));  
128 - }  
129 -  
130 - @Override  
131 - public void processClusterEvent(ActorContext context, ClusterEventMsg msg) {  
132 - if (pendingMap.size() > 0 || subscribedToAttributeUpdates || subscribedToRpcCommands) {  
133 - Optional<ServerAddress> newTargetServer = systemContext.getRoutingService().resolveById(getDeviceId());  
134 - if (!newTargetServer.equals(currentTargetServer)) {  
135 - firstMsg = true;  
136 - currentTargetServer = newTargetServer;  
137 - pendingMap.values().forEach(v -> {  
138 - forwardToAppActor(context, v, currentTargetServer);  
139 - if (currentTargetServer.isPresent()) {  
140 - logger.debug("[{}] Forwarded msg to new server: {}", sessionId, currentTargetServer.get());  
141 - } else {  
142 - logger.debug("[{}] Forwarded msg to local server.", sessionId);  
143 - }  
144 - });  
145 - if (subscribedToAttributeUpdates) {  
146 - toDeviceMsg(new AttributesSubscribeMsg()).ifPresent(m -> forwardToAppActor(context, m, currentTargetServer));  
147 - logger.debug("[{}] Forwarded attributes subscription.", sessionId);  
148 - }  
149 - if (subscribedToRpcCommands) {  
150 - toDeviceMsg(new RpcSubscribeMsg()).ifPresent(m -> forwardToAppActor(context, m, currentTargetServer));  
151 - logger.debug("[{}] Forwarded rpc commands subscription.", sessionId);  
152 - }  
153 - }  
154 - }  
155 - }  
156 -}  
1 -/**  
2 - * Copyright © 2016-2018 The Thingsboard Authors  
3 - *  
4 - * Licensed under the Apache License, Version 2.0 (the "License");  
5 - * you may not use this file except in compliance with the License.  
6 - * You may obtain a copy of the License at  
7 - *  
8 - * http://www.apache.org/licenses/LICENSE-2.0  
9 - *  
10 - * Unless required by applicable law or agreed to in writing, software  
11 - * distributed under the License is distributed on an "AS IS" BASIS,  
12 - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  
13 - * See the License for the specific language governing permissions and  
14 - * limitations under the License.  
15 - */  
16 -package org.thingsboard.server.actors.session;  
17 -  
18 -import akka.actor.ActorContext;  
19 -import akka.actor.ActorRef;  
20 -import akka.event.LoggingAdapter;  
21 -import org.thingsboard.server.actors.ActorSystemContext;  
22 -import org.thingsboard.server.actors.shared.AbstractContextAwareMsgProcessor;  
23 -import org.thingsboard.server.actors.shared.SessionTimeoutMsg;  
24 -import org.thingsboard.server.common.data.id.DeviceId;  
25 -import org.thingsboard.server.common.data.id.SessionId;  
26 -import org.thingsboard.server.common.msg.cluster.ClusterEventMsg;  
27 -import org.thingsboard.server.common.msg.cluster.SendToClusterMsg;  
28 -import org.thingsboard.server.common.msg.cluster.ServerAddress;  
29 -import org.thingsboard.server.common.msg.device.BasicDeviceToDeviceActorMsg;  
30 -import org.thingsboard.server.common.msg.device.DeviceToDeviceActorMsg;  
31 -import org.thingsboard.server.common.msg.session.AdaptorToSessionActorMsg;  
32 -import org.thingsboard.server.common.msg.session.FromDeviceMsg;  
33 -import org.thingsboard.server.common.msg.session.SessionContext;  
34 -import org.thingsboard.server.common.msg.session.SessionCtrlMsg;  
35 -import org.thingsboard.server.common.msg.session.SessionType;  
36 -import org.thingsboard.server.common.msg.session.ToDeviceMsg;  
37 -import org.thingsboard.server.common.msg.session.TransportToDeviceSessionActorMsg;  
38 -import org.thingsboard.server.common.msg.session.ctrl.SessionCloseMsg;  
39 -  
40 -import java.util.Optional;  
41 -  
42 -abstract class AbstractSessionActorMsgProcessor extends AbstractContextAwareMsgProcessor {  
43 -  
44 - protected final SessionId sessionId;  
45 - protected SessionContext sessionCtx;  
46 - protected DeviceToDeviceActorMsg deviceToDeviceActorMsgPrototype;  
47 -  
48 - protected AbstractSessionActorMsgProcessor(ActorSystemContext ctx, LoggingAdapter logger, SessionId sessionId) {  
49 - super(ctx, logger);  
50 - this.sessionId = sessionId;  
51 - }  
52 -  
53 - protected abstract void processToDeviceActorMsg(ActorContext ctx, TransportToDeviceSessionActorMsg msg);  
54 -  
55 - protected abstract void processTimeoutMsg(ActorContext context, SessionTimeoutMsg msg);  
56 -  
57 - protected abstract void processToDeviceMsg(ActorContext context, ToDeviceMsg msg);  
58 -  
59 - public abstract void processClusterEvent(ActorContext context, ClusterEventMsg msg);  
60 -  
61 - protected void processSessionCtrlMsg(ActorContext ctx, SessionCtrlMsg msg) {  
62 - if (msg instanceof SessionCloseMsg) {  
63 - cleanupSession(ctx);  
64 - terminateSession(ctx, sessionId);  
65 - }  
66 - }  
67 -  
68 - protected void cleanupSession(ActorContext ctx) {  
69 - }  
70 -  
71 - protected void updateSessionCtx(TransportToDeviceSessionActorMsg msg, SessionType type) {  
72 - sessionCtx = msg.getSessionMsg().getSessionContext();  
73 - deviceToDeviceActorMsgPrototype = new BasicDeviceToDeviceActorMsg(msg, type);  
74 - }  
75 -  
76 - protected DeviceToDeviceActorMsg toDeviceMsg(TransportToDeviceSessionActorMsg msg) {  
77 - AdaptorToSessionActorMsg adaptorMsg = msg.getSessionMsg();  
78 - return new BasicDeviceToDeviceActorMsg(deviceToDeviceActorMsgPrototype, adaptorMsg.getMsg());  
79 - }  
80 -  
81 - protected Optional<DeviceToDeviceActorMsg> toDeviceMsg(FromDeviceMsg msg) {  
82 - if (deviceToDeviceActorMsgPrototype != null) {  
83 - return Optional.of(new BasicDeviceToDeviceActorMsg(deviceToDeviceActorMsgPrototype, msg));  
84 - } else {  
85 - return Optional.empty();  
86 - }  
87 - }  
88 -  
89 - protected Optional<ServerAddress> forwardToAppActor(ActorContext ctx, DeviceToDeviceActorMsg toForward) {  
90 - Optional<ServerAddress> address = systemContext.getRoutingService().resolveById(toForward.getDeviceId());  
91 - forwardToAppActor(ctx, toForward, address);  
92 - return address;  
93 - }  
94 -  
95 - protected Optional<ServerAddress> forwardToAppActorIfAddressChanged(ActorContext ctx, DeviceToDeviceActorMsg toForward, Optional<ServerAddress> oldAddress) {  
96 -  
97 - Optional<ServerAddress> newAddress = systemContext.getRoutingService().resolveById(toForward.getDeviceId());  
98 - if (!newAddress.equals(oldAddress)) {  
99 - getAppActor().tell(new SendToClusterMsg(toForward.getDeviceId(), toForward  
100 - .toOtherAddress(systemContext.getRoutingService().getCurrentServer())), ctx.self());  
101 - }  
102 - return newAddress;  
103 - }  
104 -  
105 - protected void forwardToAppActor(ActorContext ctx, DeviceToDeviceActorMsg toForward, Optional<ServerAddress> address) {  
106 - if (address.isPresent()) {  
107 - systemContext.getRpcService().tell(systemContext.getEncodingService().convertToProtoDataMessage(address.get(),  
108 - toForward.toOtherAddress(systemContext.getRoutingService().getCurrentServer())));  
109 - } else {  
110 - getAppActor().tell(toForward, ctx.self());  
111 - }  
112 - }  
113 -  
114 - public static void terminateSession(ActorContext ctx, SessionId sessionId) {  
115 - ctx.parent().tell(new SessionTerminationMsg(sessionId), ActorRef.noSender());  
116 - ctx.stop(ctx.self());  
117 - }  
118 -  
119 - public DeviceId getDeviceId() {  
120 - return deviceToDeviceActorMsgPrototype.getDeviceId();  
121 - }  
122 -}  
1 -/**  
2 - * Copyright © 2016-2018 The Thingsboard Authors  
3 - *  
4 - * Licensed under the Apache License, Version 2.0 (the "License");  
5 - * you may not use this file except in compliance with the License.  
6 - * You may obtain a copy of the License at  
7 - *  
8 - * http://www.apache.org/licenses/LICENSE-2.0  
9 - *  
10 - * Unless required by applicable law or agreed to in writing, software  
11 - * distributed under the License is distributed on an "AS IS" BASIS,  
12 - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  
13 - * See the License for the specific language governing permissions and  
14 - * limitations under the License.  
15 - */  
16 -package org.thingsboard.server.actors.session;  
17 -  
18 -import akka.actor.OneForOneStrategy;  
19 -import akka.actor.SupervisorStrategy;  
20 -import akka.event.Logging;  
21 -import akka.event.LoggingAdapter;  
22 -import org.thingsboard.server.actors.ActorSystemContext;  
23 -import org.thingsboard.server.actors.service.ContextAwareActor;  
24 -import org.thingsboard.server.actors.service.ContextBasedCreator;  
25 -import org.thingsboard.server.actors.shared.SessionTimeoutMsg;  
26 -import org.thingsboard.server.common.data.id.SessionId;  
27 -import org.thingsboard.server.common.msg.TbActorMsg;  
28 -import org.thingsboard.server.common.msg.cluster.ClusterEventMsg;  
29 -import org.thingsboard.server.common.msg.core.ActorSystemToDeviceSessionActorMsg;  
30 -import org.thingsboard.server.common.msg.session.SessionCtrlMsg;  
31 -import org.thingsboard.server.common.msg.session.SessionMsg;  
32 -import org.thingsboard.server.common.msg.session.SessionType;  
33 -import org.thingsboard.server.common.msg.session.TransportToDeviceSessionActorMsg;  
34 -import org.thingsboard.server.common.msg.session.ctrl.SessionCloseMsg;  
35 -import scala.concurrent.duration.Duration;  
36 -  
37 -public class SessionActor extends ContextAwareActor {  
38 -  
39 - private final LoggingAdapter logger = Logging.getLogger(getContext().system(), this);  
40 -  
41 - private final SessionId sessionId;  
42 - private AbstractSessionActorMsgProcessor processor;  
43 -  
44 - private SessionActor(ActorSystemContext systemContext, SessionId sessionId) {  
45 - super(systemContext);  
46 - this.sessionId = sessionId;  
47 - }  
48 -  
49 - @Override  
50 - public SupervisorStrategy supervisorStrategy() {  
51 - return new OneForOneStrategy(-1, Duration.Inf(),  
52 - throwable -> {  
53 - logger.error(throwable, "Unknown session error");  
54 - if (throwable instanceof Error) {  
55 - return OneForOneStrategy.escalate();  
56 - } else {  
57 - return OneForOneStrategy.resume();  
58 - }  
59 - });  
60 - }  
61 -  
62 - @Override  
63 - protected boolean process(TbActorMsg msg) {  
64 - switch (msg.getMsgType()) {  
65 - case TRANSPORT_TO_DEVICE_SESSION_ACTOR_MSG:  
66 - processTransportToSessionMsg((TransportToDeviceSessionActorMsg) msg);  
67 - break;  
68 - case ACTOR_SYSTEM_TO_DEVICE_SESSION_ACTOR_MSG:  
69 - processActorsToSessionMsg((ActorSystemToDeviceSessionActorMsg) msg);  
70 - break;  
71 - case SESSION_TIMEOUT_MSG:  
72 - processTimeoutMsg((SessionTimeoutMsg) msg);  
73 - break;  
74 - case SESSION_CTRL_MSG:  
75 - processSessionCloseMsg((SessionCtrlMsg) msg);  
76 - break;  
77 - case CLUSTER_EVENT_MSG:  
78 - processClusterEvent((ClusterEventMsg) msg);  
79 - break;  
80 - default: return false;  
81 - }  
82 - return true;  
83 - }  
84 -  
85 - private void processClusterEvent(ClusterEventMsg msg) {  
86 - processor.processClusterEvent(context(), msg);  
87 - }  
88 -  
89 - private void processTransportToSessionMsg(TransportToDeviceSessionActorMsg msg) {  
90 - initProcessor(msg);  
91 - processor.processToDeviceActorMsg(context(), msg);  
92 - }  
93 -  
94 - private void processActorsToSessionMsg(ActorSystemToDeviceSessionActorMsg msg) {  
95 - processor.processToDeviceMsg(context(), msg.getMsg());  
96 - }  
97 -  
98 - private void processTimeoutMsg(SessionTimeoutMsg msg) {  
99 - if (processor != null) {  
100 - processor.processTimeoutMsg(context(), msg);  
101 - } else {  
102 - logger.warning("[{}] Can't process timeout msg: {} without processor", sessionId, msg);  
103 - }  
104 - }  
105 -  
106 - private void processSessionCloseMsg(SessionCtrlMsg msg) {  
107 - if (processor != null) {  
108 - processor.processSessionCtrlMsg(context(), msg);  
109 - } else if (msg instanceof SessionCloseMsg) {  
110 - AbstractSessionActorMsgProcessor.terminateSession(context(), sessionId);  
111 - } else {  
112 - logger.warning("[{}] Can't process session ctrl msg: {} without processor", sessionId, msg);  
113 - }  
114 - }  
115 -  
116 - private void initProcessor(TransportToDeviceSessionActorMsg msg) {  
117 - if (processor == null) {  
118 - SessionMsg sessionMsg = (SessionMsg) msg.getSessionMsg();  
119 - if (sessionMsg.getSessionContext().getSessionType() == SessionType.SYNC) {  
120 - processor = new SyncMsgProcessor(systemContext, logger, sessionId);  
121 - } else {  
122 - processor = new ASyncMsgProcessor(systemContext, logger, sessionId);  
123 - }  
124 - }  
125 - }  
126 -  
127 - public static class ActorCreator extends ContextBasedCreator<SessionActor> {  
128 - private static final long serialVersionUID = 1L;  
129 -  
130 - private final SessionId sessionId;  
131 -  
132 - public ActorCreator(ActorSystemContext context, SessionId sessionId) {  
133 - super(context);  
134 - this.sessionId = sessionId;  
135 - }  
136 -  
137 - @Override  
138 - public SessionActor create() throws Exception {  
139 - return new SessionActor(context, sessionId);  
140 - }  
141 - }  
142 -  
143 -}  
1 -/**  
2 - * Copyright © 2016-2018 The Thingsboard Authors  
3 - *  
4 - * Licensed under the Apache License, Version 2.0 (the "License");  
5 - * you may not use this file except in compliance with the License.  
6 - * You may obtain a copy of the License at  
7 - *  
8 - * http://www.apache.org/licenses/LICENSE-2.0  
9 - *  
10 - * Unless required by applicable law or agreed to in writing, software  
11 - * distributed under the License is distributed on an "AS IS" BASIS,  
12 - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  
13 - * See the License for the specific language governing permissions and  
14 - * limitations under the License.  
15 - */  
16 -package org.thingsboard.server.actors.session;  
17 -  
18 -import akka.actor.ActorInitializationException;  
19 -import akka.actor.ActorRef;  
20 -import akka.actor.InvalidActorNameException;  
21 -import akka.actor.LocalActorRef;  
22 -import akka.actor.OneForOneStrategy;  
23 -import akka.actor.Props;  
24 -import akka.actor.SupervisorStrategy;  
25 -import akka.actor.Terminated;  
26 -import akka.event.Logging;  
27 -import akka.event.LoggingAdapter;  
28 -import akka.japi.Function;  
29 -import org.thingsboard.server.actors.ActorSystemContext;  
30 -import org.thingsboard.server.actors.service.ContextAwareActor;  
31 -import org.thingsboard.server.actors.service.ContextBasedCreator;  
32 -import org.thingsboard.server.actors.service.DefaultActorService;  
33 -import org.thingsboard.server.actors.shared.SessionTimeoutMsg;  
34 -import org.thingsboard.server.common.data.id.SessionId;  
35 -import org.thingsboard.server.common.msg.TbActorMsg;  
36 -import org.thingsboard.server.common.msg.aware.SessionAwareMsg;  
37 -import org.thingsboard.server.common.msg.cluster.ClusterEventMsg;  
38 -import org.thingsboard.server.common.msg.core.ActorSystemToDeviceSessionActorMsg;  
39 -import org.thingsboard.server.common.msg.core.SessionCloseMsg;  
40 -import org.thingsboard.server.common.msg.session.SessionCtrlMsg;  
41 -import scala.concurrent.duration.Duration;  
42 -  
43 -import java.util.HashMap;  
44 -import java.util.Map;  
45 -  
46 -public class SessionManagerActor extends ContextAwareActor {  
47 -  
48 - private static final int INITIAL_SESSION_MAP_SIZE = 1024;  
49 -  
50 - private final LoggingAdapter log = Logging.getLogger(getContext().system(), this);  
51 -  
52 - private final Map<String, ActorRef> sessionActors;  
53 -  
54 - SessionManagerActor(ActorSystemContext systemContext) {  
55 - super(systemContext);  
56 - this.sessionActors = new HashMap<>(INITIAL_SESSION_MAP_SIZE);  
57 - }  
58 -  
59 - @Override  
60 - public SupervisorStrategy supervisorStrategy() {  
61 - return strategy;  
62 - }  
63 -  
64 - @Override  
65 - protected boolean process(TbActorMsg msg) {  
66 - //TODO Move everything here, to work with TbActorMsg  
67 - return false;  
68 - }  
69 -  
70 - @Override  
71 - public void onReceive(Object msg) throws Exception {  
72 - if (msg instanceof SessionCtrlMsg) {  
73 - onSessionCtrlMsg((SessionCtrlMsg) msg);  
74 - } else if (msg instanceof SessionAwareMsg) {  
75 - forwardToSessionActor((SessionAwareMsg) msg);  
76 - } else if (msg instanceof SessionTerminationMsg) {  
77 - onSessionTermination((SessionTerminationMsg) msg);  
78 - } else if (msg instanceof Terminated) {  
79 - onTermination((Terminated) msg);  
80 - } else if (msg instanceof SessionTimeoutMsg) {  
81 - onSessionTimeout((SessionTimeoutMsg) msg);  
82 - } else if (msg instanceof ClusterEventMsg) {  
83 - broadcast(msg);  
84 - }  
85 - }  
86 -  
87 - private void broadcast(Object msg) {  
88 - sessionActors.values().forEach(actorRef -> actorRef.tell(msg, ActorRef.noSender()));  
89 - }  
90 -  
91 - private void onSessionTimeout(SessionTimeoutMsg msg) {  
92 - String sessionIdStr = msg.getSessionId().toUidStr();  
93 - ActorRef sessionActor = sessionActors.get(sessionIdStr);  
94 - if (sessionActor != null) {  
95 - sessionActor.tell(msg, ActorRef.noSender());  
96 - }  
97 - }  
98 -  
99 - private void onSessionCtrlMsg(SessionCtrlMsg msg) {  
100 - String sessionIdStr = msg.getSessionId().toUidStr();  
101 - ActorRef sessionActor = sessionActors.get(sessionIdStr);  
102 - if (sessionActor != null) {  
103 - sessionActor.tell(msg, ActorRef.noSender());  
104 - }  
105 - }  
106 -  
107 - private void onSessionTermination(SessionTerminationMsg msg) {  
108 - String sessionIdStr = msg.getId().toUidStr();  
109 - ActorRef sessionActor = sessionActors.remove(sessionIdStr);  
110 - if (sessionActor != null) {  
111 - log.debug("[{}] Removed session actor.", sessionIdStr);  
112 - //TODO: onSubscriptionUpdate device actor about session close;  
113 - } else {  
114 - log.debug("[{}] Session actor was already removed.", sessionIdStr);  
115 - }  
116 - }  
117 -  
118 - private void forwardToSessionActor(SessionAwareMsg msg) {  
119 - if (msg instanceof ActorSystemToDeviceSessionActorMsg || msg instanceof SessionCloseMsg) {  
120 - String sessionIdStr = msg.getSessionId().toUidStr();  
121 - ActorRef sessionActor = sessionActors.get(sessionIdStr);  
122 - if (sessionActor != null) {  
123 - sessionActor.tell(msg, ActorRef.noSender());  
124 - } else {  
125 - log.debug("[{}] Session actor was already removed.", sessionIdStr);  
126 - }  
127 - } else {  
128 - try {  
129 - getOrCreateSessionActor(msg.getSessionId()).tell(msg, self());  
130 - } catch (InvalidActorNameException e) {  
131 - log.info("Invalid msg : {}", msg);  
132 - }  
133 - }  
134 - }  
135 -  
136 - private ActorRef getOrCreateSessionActor(SessionId sessionId) {  
137 - String sessionIdStr = sessionId.toUidStr();  
138 - ActorRef sessionActor = sessionActors.get(sessionIdStr);  
139 - if (sessionActor == null) {  
140 - log.debug("[{}] Creating session actor.", sessionIdStr);  
141 - sessionActor = context().actorOf(  
142 - Props.create(new SessionActor.ActorCreator(systemContext, sessionId)).withDispatcher(DefaultActorService.SESSION_DISPATCHER_NAME),  
143 - sessionIdStr);  
144 - sessionActors.put(sessionIdStr, sessionActor);  
145 - log.debug("[{}] Created session actor.", sessionIdStr);  
146 - }  
147 - return sessionActor;  
148 - }  
149 -  
150 - private void onTermination(Terminated message) {  
151 - ActorRef terminated = message.actor();  
152 - if (terminated instanceof LocalActorRef) {  
153 - log.info("Removed actor: {}.", terminated);  
154 - //TODO: cleanup session actors map  
155 - } else {  
156 - throw new IllegalStateException("Remote actors are not supported!");  
157 - }  
158 - }  
159 -  
160 - public static class ActorCreator extends ContextBasedCreator<SessionManagerActor> {  
161 - private static final long serialVersionUID = 1L;  
162 -  
163 - public ActorCreator(ActorSystemContext context) {  
164 - super(context);  
165 - }  
166 -  
167 - @Override  
168 - public SessionManagerActor create() throws Exception {  
169 - return new SessionManagerActor(context);  
170 - }  
171 - }  
172 -  
173 - private final SupervisorStrategy strategy = new OneForOneStrategy(3, Duration.create("1 minute"), new Function<Throwable, SupervisorStrategy.Directive>() {  
174 - @Override  
175 - public SupervisorStrategy.Directive apply(Throwable t) {  
176 - logger.error(t, "Unknown failure");  
177 - return SupervisorStrategy.stop();  
178 - }  
179 - });  
180 -}  
1 -/**  
2 - * Copyright © 2016-2018 The Thingsboard Authors  
3 - *  
4 - * Licensed under the Apache License, Version 2.0 (the "License");  
5 - * you may not use this file except in compliance with the License.  
6 - * You may obtain a copy of the License at  
7 - *  
8 - * http://www.apache.org/licenses/LICENSE-2.0  
9 - *  
10 - * Unless required by applicable law or agreed to in writing, software  
11 - * distributed under the License is distributed on an "AS IS" BASIS,  
12 - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  
13 - * See the License for the specific language governing permissions and  
14 - * limitations under the License.  
15 - */  
16 -package org.thingsboard.server.actors.session;  
17 -  
18 -import akka.actor.ActorContext;  
19 -import akka.event.LoggingAdapter;  
20 -import org.thingsboard.server.actors.ActorSystemContext;  
21 -import org.thingsboard.server.actors.shared.SessionTimeoutMsg;  
22 -import org.thingsboard.server.common.data.id.SessionId;  
23 -import org.thingsboard.server.common.msg.cluster.ClusterEventMsg;  
24 -import org.thingsboard.server.common.msg.cluster.ServerAddress;  
25 -import org.thingsboard.server.common.msg.device.DeviceToDeviceActorMsg;  
26 -import org.thingsboard.server.common.msg.session.BasicSessionActorToAdaptorMsg;  
27 -import org.thingsboard.server.common.msg.session.SessionContext;  
28 -import org.thingsboard.server.common.msg.session.SessionType;  
29 -import org.thingsboard.server.common.msg.session.ToDeviceMsg;  
30 -import org.thingsboard.server.common.msg.session.TransportToDeviceSessionActorMsg;  
31 -import org.thingsboard.server.common.msg.session.ctrl.SessionCloseMsg;  
32 -import org.thingsboard.server.common.msg.session.ex.SessionException;  
33 -  
34 -import java.util.Optional;  
35 -  
36 -class SyncMsgProcessor extends AbstractSessionActorMsgProcessor {  
37 - private DeviceToDeviceActorMsg pendingMsg;  
38 - private Optional<ServerAddress> currentTargetServer;  
39 - private boolean pendingResponse;  
40 -  
41 - public SyncMsgProcessor(ActorSystemContext ctx, LoggingAdapter logger, SessionId sessionId) {  
42 - super(ctx, logger, sessionId);  
43 - }  
44 -  
45 - @Override  
46 - protected void processToDeviceActorMsg(ActorContext ctx, TransportToDeviceSessionActorMsg msg) {  
47 - updateSessionCtx(msg, SessionType.SYNC);  
48 - pendingMsg = toDeviceMsg(msg);  
49 - pendingResponse = true;  
50 - currentTargetServer = forwardToAppActor(ctx, pendingMsg);  
51 - scheduleMsgWithDelay(ctx, new SessionTimeoutMsg(sessionId), getTimeout(systemContext, msg.getSessionMsg().getSessionContext()), ctx.parent());  
52 - }  
53 -  
54 - public void processTimeoutMsg(ActorContext context, SessionTimeoutMsg msg) {  
55 - if (pendingResponse) {  
56 - try {  
57 - sessionCtx.onMsg(SessionCloseMsg.onTimeout(sessionId));  
58 - } catch (SessionException e) {  
59 - logger.warning("Failed to push session close msg", e);  
60 - }  
61 - terminateSession(context, this.sessionId);  
62 - }  
63 - }  
64 -  
65 - public void processToDeviceMsg(ActorContext context, ToDeviceMsg msg) {  
66 - try {  
67 - sessionCtx.onMsg(new BasicSessionActorToAdaptorMsg(this.sessionCtx, msg));  
68 - pendingResponse = false;  
69 - } catch (SessionException e) {  
70 - logger.warning("Failed to push session response msg", e);  
71 - }  
72 - terminateSession(context, this.sessionId);  
73 - }  
74 -  
75 - @Override  
76 - public void processClusterEvent(ActorContext context, ClusterEventMsg msg) {  
77 - if (pendingResponse) {  
78 - Optional<ServerAddress> newTargetServer = forwardToAppActorIfAddressChanged(context, pendingMsg, currentTargetServer);  
79 - if (logger.isDebugEnabled()) {  
80 - if (!newTargetServer.equals(currentTargetServer)) {  
81 - if (newTargetServer.isPresent()) {  
82 - logger.debug("[{}] Forwarded msg to new server: {}", sessionId, newTargetServer.get());  
83 - } else {  
84 - logger.debug("[{}] Forwarded msg to local server.", sessionId);  
85 - }  
86 - }  
87 - }  
88 - currentTargetServer = newTargetServer;  
89 - }  
90 - }  
91 -  
92 - private long getTimeout(ActorSystemContext ctx, SessionContext sessionCtx) {  
93 - return sessionCtx.getTimeout() > 0 ? sessionCtx.getTimeout() : ctx.getSyncSessionTimeout();  
94 - }  
95 -}  
@@ -87,7 +87,7 @@ public class TenantActor extends RuleChainManagerActor { @@ -87,7 +87,7 @@ public class TenantActor extends RuleChainManagerActor {
87 case DEVICE_ACTOR_TO_RULE_ENGINE_MSG: 87 case DEVICE_ACTOR_TO_RULE_ENGINE_MSG:
88 onDeviceActorToRuleEngineMsg((DeviceActorToRuleEngineMsg) msg); 88 onDeviceActorToRuleEngineMsg((DeviceActorToRuleEngineMsg) msg);
89 break; 89 break;
90 - case DEVICE_SESSION_TO_DEVICE_ACTOR_MSG: 90 + case TRANSPORT_TO_DEVICE_ACTOR_MSG:
91 case DEVICE_ATTRIBUTES_UPDATE_TO_DEVICE_ACTOR_MSG: 91 case DEVICE_ATTRIBUTES_UPDATE_TO_DEVICE_ACTOR_MSG:
92 case DEVICE_CREDENTIALS_UPDATE_TO_DEVICE_ACTOR_MSG: 92 case DEVICE_CREDENTIALS_UPDATE_TO_DEVICE_ACTOR_MSG:
93 case DEVICE_NAME_OR_TYPE_UPDATE_TO_DEVICE_ACTOR_MSG: 93 case DEVICE_NAME_OR_TYPE_UPDATE_TO_DEVICE_ACTOR_MSG:
  1 +package org.thingsboard.server.service.transport;
  2 +
  3 +import akka.actor.ActorRef;
  4 +import com.fasterxml.jackson.databind.ObjectMapper;
  5 +import lombok.extern.slf4j.Slf4j;
  6 +import org.apache.kafka.clients.consumer.ConsumerRecords;
  7 +import org.apache.kafka.clients.producer.Callback;
  8 +import org.apache.kafka.clients.producer.RecordMetadata;
  9 +import org.springframework.beans.factory.annotation.Autowired;
  10 +import org.springframework.beans.factory.annotation.Value;
  11 +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
  12 +import org.springframework.stereotype.Service;
  13 +import org.thingsboard.server.actors.ActorSystemContext;
  14 +import org.thingsboard.server.actors.service.ActorService;
  15 +import org.thingsboard.server.common.msg.cluster.ServerAddress;
  16 +import org.thingsboard.server.gen.transport.TransportProtos.DeviceActorToTransportMsg;
  17 +import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg;
  18 +import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg;
  19 +import org.thingsboard.server.gen.transport.TransportProtos.TransportToDeviceActorMsg;
  20 +import org.thingsboard.server.kafka.TBKafkaConsumerTemplate;
  21 +import org.thingsboard.server.kafka.TBKafkaProducerTemplate;
  22 +import org.thingsboard.server.kafka.TbKafkaSettings;
  23 +import org.thingsboard.server.service.cluster.discovery.DiscoveryService;
  24 +import org.thingsboard.server.service.cluster.routing.ClusterRoutingService;
  25 +import org.thingsboard.server.service.cluster.rpc.ClusterRpcService;
  26 +import org.thingsboard.server.service.encoding.DataDecodingEncodingService;
  27 +import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper;
  28 +
  29 +import javax.annotation.PostConstruct;
  30 +import javax.annotation.PreDestroy;
  31 +import java.time.Duration;
  32 +import java.util.Optional;
  33 +import java.util.concurrent.ExecutorService;
  34 +import java.util.concurrent.Executors;
  35 +import java.util.function.Consumer;
  36 +
  37 +/**
  38 + * Created by ashvayka on 09.10.18.
  39 + */
  40 +@Slf4j
  41 +@Service
  42 +@ConditionalOnProperty(prefix = "transport.remote", value = "enabled", havingValue = "true")
  43 +public class RemoteRuleEngineTransportService implements RuleEngineTransportService {
  44 +
  45 + private static final ObjectMapper mapper = new ObjectMapper();
  46 +
  47 + @Value("${transport.remote.rule_engine.topic}")
  48 + private String ruleEngineTopic;
  49 + @Value("${transport.remote.notifications.topic}")
  50 + private String notificationsTopic;
  51 + @Value("${transport.remote.rule_engine.poll_interval}")
  52 + private int pollDuration;
  53 + @Value("${transport.remote.rule_engine.auto_commit_interval}")
  54 + private int autoCommitInterval;
  55 +
  56 + @Autowired
  57 + private TbKafkaSettings kafkaSettings;
  58 +
  59 + @Autowired
  60 + private DiscoveryService discoveryService;
  61 +
  62 + @Autowired
  63 + private ActorSystemContext actorContext;
  64 +
  65 + @Autowired
  66 + private ActorService actorService;
  67 +
  68 + //TODO: completely replace this routing with the Kafka routing by partition ids.
  69 + @Autowired
  70 + private ClusterRoutingService routingService;
  71 + @Autowired
  72 + private ClusterRpcService rpcService;
  73 + @Autowired
  74 + private DataDecodingEncodingService encodingService;
  75 +
  76 + private TBKafkaConsumerTemplate<ToRuleEngineMsg> ruleEngineConsumer;
  77 + private TBKafkaProducerTemplate<ToTransportMsg> notificationsProducer;
  78 +
  79 + private ExecutorService mainConsumerExecutor = Executors.newSingleThreadExecutor();
  80 +
  81 + private volatile boolean stopped = false;
  82 +
  83 + @PostConstruct
  84 + public void init() {
  85 + TBKafkaProducerTemplate.TBKafkaProducerTemplateBuilder<ToTransportMsg> notificationsProducerBuilder = TBKafkaProducerTemplate.builder();
  86 + notificationsProducerBuilder.settings(kafkaSettings);
  87 + notificationsProducerBuilder.defaultTopic(notificationsTopic);
  88 + notificationsProducerBuilder.encoder(new ToTransportMsgEncoder());
  89 +
  90 + notificationsProducer = notificationsProducerBuilder.build();
  91 + notificationsProducer.init();
  92 +
  93 + TBKafkaConsumerTemplate.TBKafkaConsumerTemplateBuilder<ToRuleEngineMsg> ruleEngineConsumerBuilder = TBKafkaConsumerTemplate.builder();
  94 + ruleEngineConsumerBuilder.settings(kafkaSettings);
  95 + ruleEngineConsumerBuilder.topic(ruleEngineTopic);
  96 + ruleEngineConsumerBuilder.clientId(discoveryService.getNodeId());
  97 + ruleEngineConsumerBuilder.groupId("tb-node");
  98 + ruleEngineConsumerBuilder.autoCommit(true);
  99 + ruleEngineConsumerBuilder.autoCommitIntervalMs(autoCommitInterval);
  100 + ruleEngineConsumerBuilder.decoder(new ToRuleEngineMsgDecoder());
  101 +
  102 + ruleEngineConsumer = ruleEngineConsumerBuilder.build();
  103 + ruleEngineConsumer.subscribe();
  104 +
  105 + mainConsumerExecutor.execute(() -> {
  106 + while (!stopped) {
  107 + try {
  108 + ConsumerRecords<String, byte[]> records = ruleEngineConsumer.poll(Duration.ofMillis(pollDuration));
  109 + records.forEach(record -> {
  110 + try {
  111 + ToRuleEngineMsg toRuleEngineMsg = ruleEngineConsumer.decode(record);
  112 + if (toRuleEngineMsg.hasToDeviceActorMsg()) {
  113 + forwardToDeviceActor(toRuleEngineMsg.getToDeviceActorMsg());
  114 + }
  115 + } catch (Throwable e) {
  116 + log.warn("Failed to process the notification.", e);
  117 + }
  118 + });
  119 + } catch (Exception e) {
  120 + log.warn("Failed to obtain messages from queue.", e);
  121 + try {
  122 + Thread.sleep(pollDuration);
  123 + } catch (InterruptedException e2) {
  124 + log.trace("Failed to wait until the server has capacity to handle new requests", e2);
  125 + }
  126 + }
  127 + }
  128 + });
  129 + }
  130 +
  131 + @Override
  132 + public void process(String nodeId, DeviceActorToTransportMsg msg) {
  133 + process(nodeId, msg, null, null);
  134 + }
  135 +
  136 + @Override
  137 + public void process(String nodeId, DeviceActorToTransportMsg msg, Runnable onSuccess, Consumer<Throwable> onFailure) {
  138 + notificationsProducer.send(notificationsTopic + "." + nodeId,
  139 + ToTransportMsg.newBuilder().setToDeviceSessionMsg(msg).build()
  140 + , new QueueCallbackAdaptor(onSuccess, onFailure));
  141 + }
  142 +
  143 + private void forwardToDeviceActor(TransportToDeviceActorMsg toDeviceActorMsg) {
  144 + TransportToDeviceActorMsgWrapper wrapper = new TransportToDeviceActorMsgWrapper(toDeviceActorMsg);
  145 + Optional<ServerAddress> address = routingService.resolveById(wrapper.getDeviceId());
  146 + if (address.isPresent()) {
  147 + rpcService.tell(encodingService.convertToProtoDataMessage(address.get(), wrapper));
  148 + } else {
  149 + actorContext.getAppActor().tell(wrapper, ActorRef.noSender());
  150 + }
  151 + }
  152 +
  153 + @PreDestroy
  154 + public void destroy() {
  155 + stopped = true;
  156 + if (ruleEngineConsumer != null) {
  157 + ruleEngineConsumer.unsubscribe();
  158 + }
  159 + if (mainConsumerExecutor != null) {
  160 + mainConsumerExecutor.shutdownNow();
  161 + }
  162 + }
  163 +
  164 + private static class QueueCallbackAdaptor implements Callback {
  165 + private final Runnable onSuccess;
  166 + private final Consumer<Throwable> onFailure;
  167 +
  168 + QueueCallbackAdaptor(Runnable onSuccess, Consumer<Throwable> onFailure) {
  169 + this.onSuccess = onSuccess;
  170 + this.onFailure = onFailure;
  171 + }
  172 +
  173 + @Override
  174 + public void onCompletion(RecordMetadata metadata, Exception exception) {
  175 + if (exception == null) {
  176 + if (onSuccess != null) {
  177 + onSuccess.run();
  178 + }
  179 + } else {
  180 + if (onFailure != null) {
  181 + onFailure.accept(exception);
  182 + }
  183 + }
  184 + }
  185 + }
  186 +
  187 +}
  1 +/**
  2 + * Copyright © 2016-2018 The Thingsboard Authors
  3 + * <p>
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + * <p>
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + * <p>
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.service.transport;
  17 +
  18 +import org.thingsboard.server.gen.transport.TransportProtos.DeviceActorToTransportMsg;
  19 +import org.thingsboard.server.gen.transport.TransportProtos.;
  20 +
  21 +import java.util.function.Consumer;
  22 +
  23 +/**
  24 + * Created by ashvayka on 05.10.18.
  25 + */
  26 +public interface RuleEngineTransportService {
  27 +
  28 + void process(String nodeId, DeviceActorToTransportMsg msg);
  29 +
  30 + void process(String nodeId, DeviceActorToTransportMsg msg, Runnable onSuccess, Consumer<Throwable> onFailure);
  31 +
  32 +}
application/src/main/java/org/thingsboard/server/service/transport/ToRuleEngineMsgDecoder.java renamed from application/src/main/java/org/thingsboard/server/actors/session/SessionTerminationMsg.java
@@ -13,14 +13,19 @@ @@ -13,14 +13,19 @@
13 * See the License for the specific language governing permissions and 13 * See the License for the specific language governing permissions and
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 -package org.thingsboard.server.actors.session; 16 +package org.thingsboard.server.service.transport;
17 17
18 -import org.thingsboard.server.actors.shared.ActorTerminationMsg;  
19 -import org.thingsboard.server.common.data.id.SessionId; 18 +import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg;
  19 +import org.thingsboard.server.kafka.TbKafkaDecoder;
20 20
21 -public class SessionTerminationMsg extends ActorTerminationMsg<SessionId> { 21 +import java.io.IOException;
22 22
23 - public SessionTerminationMsg(SessionId id) {  
24 - super(id); 23 +/**
  24 + * Created by ashvayka on 05.10.18.
  25 + */
  26 +public class ToRuleEngineMsgDecoder implements TbKafkaDecoder<ToRuleEngineMsg> {
  27 + @Override
  28 + public ToRuleEngineMsg decode(byte[] data) throws IOException {
  29 + return ToRuleEngineMsg.parseFrom(data);
25 } 30 }
26 } 31 }
  1 +/**
  2 + * Copyright © 2016-2018 The Thingsboard Authors
  3 + * <p>
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + * <p>
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + * <p>
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.service.transport;
  17 +
  18 +import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg;
  19 +import org.thingsboard.server.kafka.TbKafkaEncoder;
  20 +
  21 +/**
  22 + * Created by ashvayka on 05.10.18.
  23 + */
  24 +public class ToTransportMsgEncoder implements TbKafkaEncoder<ToTransportMsg> {
  25 + @Override
  26 + public byte[] encode(ToTransportMsg value) {
  27 + return value.toByteArray();
  28 + }
  29 +}
  1 +package org.thingsboard.server.service.transport.msg;
  2 +
  3 +import lombok.Data;
  4 +import org.thingsboard.server.common.data.id.DeviceId;
  5 +import org.thingsboard.server.common.data.id.TenantId;
  6 +import org.thingsboard.server.common.msg.MsgType;
  7 +import org.thingsboard.server.common.msg.TbActorMsg;
  8 +import org.thingsboard.server.common.msg.aware.DeviceAwareMsg;
  9 +import org.thingsboard.server.common.msg.aware.TenantAwareMsg;
  10 +import org.thingsboard.server.common.msg.cluster.ServerAddress;
  11 +import org.thingsboard.server.gen.transport.TransportProtos.TransportToDeviceActorMsg;
  12 +
  13 +import java.io.Serializable;
  14 +import java.util.UUID;
  15 +
  16 +/**
  17 + * Created by ashvayka on 09.10.18.
  18 + */
  19 +@Data
  20 +public class TransportToDeviceActorMsgWrapper implements TbActorMsg, DeviceAwareMsg, TenantAwareMsg, Serializable {
  21 +
  22 + private final TenantId tenantId;
  23 + private final DeviceId deviceId;
  24 + private final TransportToDeviceActorMsg msg;
  25 +
  26 + public TransportToDeviceActorMsgWrapper(TransportToDeviceActorMsg msg) {
  27 + this.msg = msg;
  28 + this.tenantId = new TenantId(new UUID(msg.getSessionInfo().getTenantIdMSB(), msg.getSessionInfo().getTenantIdLSB()));
  29 + this.deviceId = new DeviceId(new UUID(msg.getSessionInfo().getDeviceIdMSB(), msg.getSessionInfo().getDeviceIdLSB()));
  30 + }
  31 +
  32 + @Override
  33 + public MsgType getMsgType() {
  34 + return MsgType.TRANSPORT_TO_DEVICE_ACTOR_MSG;
  35 + }
  36 +}
@@ -462,4 +462,8 @@ transport: @@ -462,4 +462,8 @@ transport:
462 request_poll_interval: "${TB_TRANSPORT_RESPONSE_POLL_INTERVAL_MS:25}" 462 request_poll_interval: "${TB_TRANSPORT_RESPONSE_POLL_INTERVAL_MS:25}"
463 request_auto_commit_interval: "${TB_TRANSPORT_RESPONSE_AUTO_COMMIT_INTERVAL_MS:1000}" 463 request_auto_commit_interval: "${TB_TRANSPORT_RESPONSE_AUTO_COMMIT_INTERVAL_MS:1000}"
464 rule_engine: 464 rule_engine:
465 - topic: "${TB_RULE_ENGINE_TOPIC:tb.rule-engine}"  
  465 + topic: "${TB_RULE_ENGINE_TOPIC:tb.rule-engine}"
  466 + poll_interval: "${TB_RULE_ENGINE_POLL_INTERVAL_MS:25}"
  467 + auto_commit_interval: "${TB_RULE_ENGINE_AUTO_COMMIT_INTERVAL_MS:100}"
  468 + notifications:
  469 + topic: "${TB_TRANSPORT_NOTIFICATIONS_TOPIC:tb.transport.notifications}"
@@ -77,11 +77,6 @@ public enum MsgType { @@ -77,11 +77,6 @@ public enum MsgType {
77 */ 77 */
78 RULE_TO_SELF_MSG, 78 RULE_TO_SELF_MSG,
79 79
80 - /**  
81 - * Message that is sent by Session Actor to Device Actor. Represents messages from the device itself.  
82 - */  
83 - DEVICE_SESSION_TO_DEVICE_ACTOR_MSG,  
84 -  
85 DEVICE_ATTRIBUTES_UPDATE_TO_DEVICE_ACTOR_MSG, 80 DEVICE_ATTRIBUTES_UPDATE_TO_DEVICE_ACTOR_MSG,
86 81
87 DEVICE_CREDENTIALS_UPDATE_TO_DEVICE_ACTOR_MSG, 82 DEVICE_CREDENTIALS_UPDATE_TO_DEVICE_ACTOR_MSG,
@@ -111,6 +106,12 @@ public enum MsgType { @@ -111,6 +106,12 @@ public enum MsgType {
111 TRANSPORT_TO_DEVICE_SESSION_ACTOR_MSG, 106 TRANSPORT_TO_DEVICE_SESSION_ACTOR_MSG,
112 SESSION_TIMEOUT_MSG, 107 SESSION_TIMEOUT_MSG,
113 SESSION_CTRL_MSG, 108 SESSION_CTRL_MSG,
114 - STATS_PERSIST_TICK_MSG; 109 + STATS_PERSIST_TICK_MSG,
  110 +
  111 +
  112 + /**
  113 + * Message that is sent by TransportRuleEngineService to Device Actor. Represents messages from the device itself.
  114 + */
  115 + TRANSPORT_TO_DEVICE_ACTOR_MSG;
115 116
116 } 117 }
1 -/**  
2 - * Copyright © 2016-2018 The Thingsboard Authors  
3 - *  
4 - * Licensed under the Apache License, Version 2.0 (the "License");  
5 - * you may not use this file except in compliance with the License.  
6 - * You may obtain a copy of the License at  
7 - *  
8 - * http://www.apache.org/licenses/LICENSE-2.0  
9 - *  
10 - * Unless required by applicable law or agreed to in writing, software  
11 - * distributed under the License is distributed on an "AS IS" BASIS,  
12 - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  
13 - * See the License for the specific language governing permissions and  
14 - * limitations under the License.  
15 - */  
16 -package org.thingsboard.server.common.msg.device;  
17 -  
18 -import lombok.ToString;  
19 -import org.thingsboard.server.common.data.id.CustomerId;  
20 -import org.thingsboard.server.common.data.id.DeviceId;  
21 -import org.thingsboard.server.common.data.id.SessionId;  
22 -import org.thingsboard.server.common.data.id.TenantId;  
23 -import org.thingsboard.server.common.msg.MsgType;  
24 -import org.thingsboard.server.common.msg.cluster.ServerAddress;  
25 -import org.thingsboard.server.common.msg.session.FromDeviceMsg;  
26 -import org.thingsboard.server.common.msg.session.SessionType;  
27 -import org.thingsboard.server.common.msg.session.TransportToDeviceSessionActorMsg;  
28 -  
29 -import java.util.Optional;  
30 -  
31 -@ToString  
32 -public class BasicDeviceToDeviceActorMsg implements DeviceToDeviceActorMsg {  
33 -  
34 - private static final long serialVersionUID = -1866795134993115408L;  
35 -  
36 - private final TenantId tenantId;  
37 - private final CustomerId customerId;  
38 - private final DeviceId deviceId;  
39 - private final SessionId sessionId;  
40 - private final SessionType sessionType;  
41 - private final ServerAddress serverAddress;  
42 - private final FromDeviceMsg msg;  
43 -  
44 - public BasicDeviceToDeviceActorMsg(DeviceToDeviceActorMsg other, FromDeviceMsg msg) {  
45 - this(null, other.getTenantId(), other.getCustomerId(), other.getDeviceId(), other.getSessionId(), other.getSessionType(), msg);  
46 - }  
47 -  
48 - public BasicDeviceToDeviceActorMsg(TransportToDeviceSessionActorMsg msg, SessionType sessionType) {  
49 - this(null, msg.getTenantId(), msg.getCustomerId(), msg.getDeviceId(), msg.getSessionId(), sessionType, msg.getSessionMsg().getMsg());  
50 - }  
51 -  
52 - private BasicDeviceToDeviceActorMsg(ServerAddress serverAddress, TenantId tenantId, CustomerId customerId, DeviceId deviceId, SessionId sessionId, SessionType sessionType,  
53 - FromDeviceMsg msg) {  
54 - super();  
55 - this.serverAddress = serverAddress;  
56 - this.tenantId = tenantId;  
57 - this.customerId = customerId;  
58 - this.deviceId = deviceId;  
59 - this.sessionId = sessionId;  
60 - this.sessionType = sessionType;  
61 - this.msg = msg;  
62 - }  
63 -  
64 - @Override  
65 - public DeviceId getDeviceId() {  
66 - return deviceId;  
67 - }  
68 -  
69 - @Override  
70 - public CustomerId getCustomerId() {  
71 - return customerId;  
72 - }  
73 -  
74 - public TenantId getTenantId() {  
75 - return tenantId;  
76 - }  
77 -  
78 - @Override  
79 - public SessionId getSessionId() {  
80 - return sessionId;  
81 - }  
82 -  
83 - @Override  
84 - public SessionType getSessionType() {  
85 - return sessionType;  
86 - }  
87 -  
88 - @Override  
89 - public Optional<ServerAddress> getServerAddress() {  
90 - return Optional.ofNullable(serverAddress);  
91 - }  
92 -  
93 - @Override  
94 - public FromDeviceMsg getPayload() {  
95 - return msg;  
96 - }  
97 -  
98 - @Override  
99 - public DeviceToDeviceActorMsg toOtherAddress(ServerAddress otherAddress) {  
100 - return new BasicDeviceToDeviceActorMsg(otherAddress, tenantId, customerId, deviceId, sessionId, sessionType, msg);  
101 - }  
102 -  
103 - @Override  
104 - public MsgType getMsgType() {  
105 - return MsgType.DEVICE_SESSION_TO_DEVICE_ACTOR_MSG;  
106 - }  
107 -}  
1 /** 1 /**
2 * Copyright © 2016-2018 The Thingsboard Authors 2 * Copyright © 2016-2018 The Thingsboard Authors
3 - * 3 + * <p>
4 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License. 5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at 6 * You may obtain a copy of the License at
7 - *  
8 - * http://www.apache.org/licenses/LICENSE-2.0  
9 - * 7 + * <p>
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + * <p>
10 * Unless required by applicable law or agreed to in writing, software 10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS, 11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -49,7 +49,9 @@ public class TBKafkaConsumerTemplate<T> { @@ -49,7 +49,9 @@ public class TBKafkaConsumerTemplate<T> {
49 boolean autoCommit, int autoCommitIntervalMs) { 49 boolean autoCommit, int autoCommitIntervalMs) {
50 Properties props = settings.toProps(); 50 Properties props = settings.toProps();
51 props.put(ConsumerConfig.CLIENT_ID_CONFIG, clientId); 51 props.put(ConsumerConfig.CLIENT_ID_CONFIG, clientId);
52 - props.put(ConsumerConfig.GROUP_ID_CONFIG, groupId); 52 + if (groupId != null) {
  53 + props.put(ConsumerConfig.GROUP_ID_CONFIG, groupId);
  54 + }
53 props.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, autoCommit); 55 props.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, autoCommit);
54 props.put(ConsumerConfig.AUTO_COMMIT_INTERVAL_MS_CONFIG, autoCommitIntervalMs); 56 props.put(ConsumerConfig.AUTO_COMMIT_INTERVAL_MS_CONFIG, autoCommitIntervalMs);
55 props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringDeserializer"); 57 props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringDeserializer");
1 /** 1 /**
2 * Copyright © 2016-2018 The Thingsboard Authors 2 * Copyright © 2016-2018 The Thingsboard Authors
3 - * 3 + * <p>
4 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License. 5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at 6 * You may obtain a copy of the License at
7 - *  
8 - * http://www.apache.org/licenses/LICENSE-2.0  
9 - * 7 + * <p>
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + * <p>
10 * Unless required by applicable law or agreed to in writing, software 10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS, 11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -20,6 +20,7 @@ import lombok.Getter; @@ -20,6 +20,7 @@ import lombok.Getter;
20 import lombok.extern.slf4j.Slf4j; 20 import lombok.extern.slf4j.Slf4j;
21 import org.apache.kafka.clients.admin.CreateTopicsResult; 21 import org.apache.kafka.clients.admin.CreateTopicsResult;
22 import org.apache.kafka.clients.admin.NewTopic; 22 import org.apache.kafka.clients.admin.NewTopic;
  23 +import org.apache.kafka.clients.producer.Callback;
23 import org.apache.kafka.clients.producer.KafkaProducer; 24 import org.apache.kafka.clients.producer.KafkaProducer;
24 import org.apache.kafka.clients.producer.ProducerConfig; 25 import org.apache.kafka.clients.producer.ProducerConfig;
25 import org.apache.kafka.clients.producer.ProducerRecord; 26 import org.apache.kafka.clients.producer.ProducerRecord;
@@ -89,28 +90,28 @@ public class TBKafkaProducerTemplate<T> { @@ -89,28 +90,28 @@ public class TBKafkaProducerTemplate<T> {
89 } 90 }
90 } 91 }
91 92
92 - public Future<RecordMetadata> send(String key, T value) {  
93 - return send(key, value, null, null); 93 + public Future<RecordMetadata> send(String key, T value, Callback callback) {
  94 + return send(key, value, null, callback);
94 } 95 }
95 96
96 - public Future<RecordMetadata> send(String key, T value, Iterable<Header> headers) {  
97 - return send(key, value, null, headers); 97 + public Future<RecordMetadata> send(String key, T value, Iterable<Header> headers, Callback callback) {
  98 + return send(key, value, null, headers, callback);
98 } 99 }
99 100
100 - public Future<RecordMetadata> send(String key, T value, Long timestamp, Iterable<Header> headers) {  
101 - return send(this.defaultTopic, key, value, timestamp, headers); 101 + public Future<RecordMetadata> send(String key, T value, Long timestamp, Iterable<Header> headers, Callback callback) {
  102 + return send(this.defaultTopic, key, value, timestamp, headers, callback);
102 } 103 }
103 104
104 - public Future<RecordMetadata> send(String topic, String key, T value, Iterable<Header> headers) {  
105 - return send(topic, key, value, null, headers); 105 + public Future<RecordMetadata> send(String topic, String key, T value, Iterable<Header> headers, Callback callback) {
  106 + return send(topic, key, value, null, headers, callback);
106 } 107 }
107 108
108 - public Future<RecordMetadata> send(String topic, String key, T value, Long timestamp, Iterable<Header> headers) { 109 + public Future<RecordMetadata> send(String topic, String key, T value, Long timestamp, Iterable<Header> headers, Callback callback) {
109 byte[] data = encoder.encode(value); 110 byte[] data = encoder.encode(value);
110 ProducerRecord<String, byte[]> record; 111 ProducerRecord<String, byte[]> record;
111 Integer partition = getPartition(topic, key, value, data); 112 Integer partition = getPartition(topic, key, value, data);
112 record = new ProducerRecord<>(topic, partition, timestamp, key, data, headers); 113 record = new ProducerRecord<>(topic, partition, timestamp, key, data, headers);
113 - return producer.send(record); 114 + return producer.send(record, callback);
114 } 115 }
115 116
116 private Integer getPartition(String topic, String key, T value, byte[] data) { 117 private Integer getPartition(String topic, String key, T value, byte[] data) {
1 /** 1 /**
2 * Copyright © 2016-2018 The Thingsboard Authors 2 * Copyright © 2016-2018 The Thingsboard Authors
3 - * 3 + * <p>
4 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License. 5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at 6 * You may obtain a copy of the License at
7 - *  
8 - * http://www.apache.org/licenses/LICENSE-2.0  
9 - * 7 + * <p>
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + * <p>
10 * Unless required by applicable law or agreed to in writing, software 10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS, 11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -77,55 +77,64 @@ public class TbKafkaResponseTemplate<Request, Response> extends AbstractTbKafkaT @@ -77,55 +77,64 @@ public class TbKafkaResponseTemplate<Request, Response> extends AbstractTbKafkaT
77 requestTemplate.subscribe(); 77 requestTemplate.subscribe();
78 loopExecutor.submit(() -> { 78 loopExecutor.submit(() -> {
79 while (!stopped) { 79 while (!stopped) {
80 - while (pendingRequestCount.get() >= maxPendingRequests) { 80 + try {
  81 + while (pendingRequestCount.get() >= maxPendingRequests) {
  82 + try {
  83 + Thread.sleep(pollInterval);
  84 + } catch (InterruptedException e) {
  85 + log.trace("Failed to wait until the server has capacity to handle new requests", e);
  86 + }
  87 + }
  88 + ConsumerRecords<String, byte[]> requests = requestTemplate.poll(Duration.ofMillis(pollInterval));
  89 + requests.forEach(request -> {
  90 + Header requestIdHeader = request.headers().lastHeader(TbKafkaSettings.REQUEST_ID_HEADER);
  91 + if (requestIdHeader == null) {
  92 + log.error("[{}] Missing requestId in header", request);
  93 + return;
  94 + }
  95 + UUID requestId = bytesToUuid(requestIdHeader.value());
  96 + if (requestId == null) {
  97 + log.error("[{}] Missing requestId in header and body", request);
  98 + return;
  99 + }
  100 + Header responseTopicHeader = request.headers().lastHeader(TbKafkaSettings.RESPONSE_TOPIC_HEADER);
  101 + if (responseTopicHeader == null) {
  102 + log.error("[{}] Missing response topic in header", request);
  103 + return;
  104 + }
  105 + String responseTopic = bytesToString(responseTopicHeader.value());
  106 + try {
  107 + pendingRequestCount.getAndIncrement();
  108 + Request decodedRequest = requestTemplate.decode(request);
  109 + AsyncCallbackTemplate.withCallbackAndTimeout(handler.handle(decodedRequest),
  110 + response -> {
  111 + pendingRequestCount.decrementAndGet();
  112 + reply(requestId, responseTopic, response);
  113 + },
  114 + e -> {
  115 + pendingRequestCount.decrementAndGet();
  116 + if (e.getCause() != null && e.getCause() instanceof TimeoutException) {
  117 + log.warn("[{}] Timedout to process the request: {}", requestId, request, e);
  118 + } else {
  119 + log.trace("[{}] Failed to process the request: {}", requestId, request, e);
  120 + }
  121 + },
  122 + requestTimeout,
  123 + timeoutExecutor,
  124 + callbackExecutor);
  125 + } catch (Throwable e) {
  126 + pendingRequestCount.decrementAndGet();
  127 + log.warn("[{}] Failed to process the request: {}", requestId, request, e);
  128 + }
  129 + });
  130 + } catch (Throwable e) {
  131 + log.warn("Failed to obtain messages from queue.", e);
81 try { 132 try {
82 Thread.sleep(pollInterval); 133 Thread.sleep(pollInterval);
83 - } catch (InterruptedException e) {  
84 - log.trace("Failed to wait until the server has capacity to handle new requests", e); 134 + } catch (InterruptedException e2) {
  135 + log.trace("Failed to wait until the server has capacity to handle new requests", e2);
85 } 136 }
86 } 137 }
87 - ConsumerRecords<String, byte[]> requests = requestTemplate.poll(Duration.ofMillis(pollInterval));  
88 - requests.forEach(request -> {  
89 - Header requestIdHeader = request.headers().lastHeader(TbKafkaSettings.REQUEST_ID_HEADER);  
90 - if (requestIdHeader == null) {  
91 - log.error("[{}] Missing requestId in header", request);  
92 - return;  
93 - }  
94 - UUID requestId = bytesToUuid(requestIdHeader.value());  
95 - if (requestId == null) {  
96 - log.error("[{}] Missing requestId in header and body", request);  
97 - return;  
98 - }  
99 - Header responseTopicHeader = request.headers().lastHeader(TbKafkaSettings.RESPONSE_TOPIC_HEADER);  
100 - if (responseTopicHeader == null) {  
101 - log.error("[{}] Missing response topic in header", request);  
102 - return;  
103 - }  
104 - String responseTopic = bytesToString(responseTopicHeader.value());  
105 - try {  
106 - pendingRequestCount.getAndIncrement();  
107 - Request decodedRequest = requestTemplate.decode(request);  
108 - AsyncCallbackTemplate.withCallbackAndTimeout(handler.handle(decodedRequest),  
109 - response -> {  
110 - pendingRequestCount.decrementAndGet();  
111 - reply(requestId, responseTopic, response);  
112 - },  
113 - e -> {  
114 - pendingRequestCount.decrementAndGet();  
115 - if (e.getCause() != null && e.getCause() instanceof TimeoutException) {  
116 - log.warn("[{}] Timedout to process the request: {}", requestId, request, e);  
117 - } else {  
118 - log.trace("[{}] Failed to process the request: {}", requestId, request, e);  
119 - }  
120 - },  
121 - requestTimeout,  
122 - timeoutExecutor,  
123 - callbackExecutor);  
124 - } catch (Throwable e) {  
125 - pendingRequestCount.decrementAndGet();  
126 - log.warn("[{}] Failed to process the request: {}", requestId, request, e);  
127 - }  
128 - });  
129 } 138 }
130 }); 139 });
131 } 140 }
@@ -141,7 +150,7 @@ public class TbKafkaResponseTemplate<Request, Response> extends AbstractTbKafkaT @@ -141,7 +150,7 @@ public class TbKafkaResponseTemplate<Request, Response> extends AbstractTbKafkaT
141 } 150 }
142 151
143 private void reply(UUID requestId, String topic, Response response) { 152 private void reply(UUID requestId, String topic, Response response) {
144 - responseTemplate.send(topic, requestId.toString(), response, Collections.singletonList(new RecordHeader(TbKafkaSettings.REQUEST_ID_HEADER, uuidToBytes(requestId)))); 153 + responseTemplate.send(topic, requestId.toString(), response, Collections.singletonList(new RecordHeader(TbKafkaSettings.REQUEST_ID_HEADER, uuidToBytes(requestId))), null);
145 } 154 }
146 155
147 } 156 }
  1 +/**
  2 + * Copyright © 2016-2018 The Thingsboard Authors
  3 + * <p>
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + * <p>
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + * <p>
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.transport;
  17 +
  18 +import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeResponseMsg;
  19 +
  20 +/**
  21 + * Created by ashvayka on 04.10.18.
  22 + */
  23 +public interface SessionMsgListener {
  24 +
  25 + void onGetAttributesResponse(GetAttributeResponseMsg getAttributesResponse);
  26 +}
1 /** 1 /**
2 * Copyright © 2016-2018 The Thingsboard Authors 2 * Copyright © 2016-2018 The Thingsboard Authors
3 - * 3 + * <p>
4 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License. 5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at 6 * You may obtain a copy of the License at
7 - *  
8 - * http://www.apache.org/licenses/LICENSE-2.0  
9 - * 7 + * <p>
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + * <p>
10 * Unless required by applicable law or agreed to in writing, software 10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS, 11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -15,23 +15,33 @@ @@ -15,23 +15,33 @@
15 */ 15 */
16 package org.thingsboard.server.common.transport; 16 package org.thingsboard.server.common.transport;
17 17
18 -import org.thingsboard.server.gen.transport.TransportProtos; 18 +import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto;
  19 +import org.thingsboard.server.gen.transport.TransportProtos.PostAttributeMsg;
  20 +import org.thingsboard.server.gen.transport.TransportProtos.PostTelemetryMsg;
  21 +import org.thingsboard.server.gen.transport.TransportProtos.SessionEventMsg;
  22 +import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceCredentialsResponseMsg;
  23 +import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceTokenRequestMsg;
  24 +import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceX509CertRequestMsg;
19 25
20 /** 26 /**
21 * Created by ashvayka on 04.10.18. 27 * Created by ashvayka on 04.10.18.
22 */ 28 */
23 public interface TransportService { 29 public interface TransportService {
24 30
25 - void process(TransportProtos.ValidateDeviceTokenRequestMsg msg,  
26 - TransportServiceCallback<TransportProtos.ValidateDeviceCredentialsResponseMsg> callback); 31 + void process(ValidateDeviceTokenRequestMsg msg,
  32 + TransportServiceCallback<ValidateDeviceCredentialsResponseMsg> callback);
27 33
28 - void process(TransportProtos.ValidateDeviceX509CertRequestMsg msg,  
29 - TransportServiceCallback<TransportProtos.ValidateDeviceCredentialsResponseMsg> callback); 34 + void process(ValidateDeviceX509CertRequestMsg msg,
  35 + TransportServiceCallback<ValidateDeviceCredentialsResponseMsg> callback);
30 36
31 - void process(TransportProtos.SessionEventMsg msg, TransportServiceCallback<Void> callback); 37 + void process(SessionInfoProto sessionInfo, SessionEventMsg msg, TransportServiceCallback<Void> callback);
32 38
33 - void process(TransportProtos.PostTelemetryMsg msg, TransportServiceCallback<Void> callback); 39 + void process(SessionInfoProto sessionInfo, PostTelemetryMsg msg, TransportServiceCallback<Void> callback);
34 40
35 - void process(TransportProtos.PostAttributeMsg msg, TransportServiceCallback<Void> callback); 41 + void process(SessionInfoProto sessionInfo, PostAttributeMsg msg, TransportServiceCallback<Void> callback);
  42 +
  43 + void registerSession(SessionInfoProto sessionInfo, SessionMsgListener listener);
  44 +
  45 + void deregisterSession(SessionInfoProto sessionInfo);
36 46
37 } 47 }
1 /** 1 /**
2 * Copyright © 2016-2018 The Thingsboard Authors 2 * Copyright © 2016-2018 The Thingsboard Authors
3 - * 3 + * <p>
4 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License. 5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at 6 * You may obtain a copy of the License at
7 - *  
8 - * http://www.apache.org/licenses/LICENSE-2.0  
9 - * 7 + * <p>
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + * <p>
10 * Unless required by applicable law or agreed to in writing, software 10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS, 11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -16,17 +16,12 @@ @@ -16,17 +16,12 @@
16 package org.thingsboard.server.common.transport.session; 16 package org.thingsboard.server.common.transport.session;
17 17
18 import lombok.Data; 18 import lombok.Data;
19 -import lombok.extern.slf4j.Slf4j;  
20 -import org.thingsboard.server.common.data.Device;  
21 -import org.thingsboard.server.common.data.security.DeviceCredentialsFilter;  
22 -import org.thingsboard.server.common.data.security.DeviceTokenCredentials; 19 +import lombok.Getter;
  20 +import org.thingsboard.server.common.data.id.DeviceId;
23 import org.thingsboard.server.common.msg.session.SessionContext; 21 import org.thingsboard.server.common.msg.session.SessionContext;
24 -import org.thingsboard.server.common.transport.SessionMsgProcessor;  
25 -import org.thingsboard.server.common.transport.auth.DeviceAuthResult;  
26 -import org.thingsboard.server.common.transport.auth.DeviceAuthService;  
27 -import org.thingsboard.server.gen.transport.TransportProtos; 22 +import org.thingsboard.server.gen.transport.TransportProtos.DeviceInfoProto;
28 23
29 -import java.util.Optional; 24 +import java.util.UUID;
30 25
31 /** 26 /**
32 * @author Andrew Shvayka 27 * @author Andrew Shvayka
@@ -34,7 +29,9 @@ import java.util.Optional; @@ -34,7 +29,9 @@ import java.util.Optional;
34 @Data 29 @Data
35 public abstract class DeviceAwareSessionContext implements SessionContext { 30 public abstract class DeviceAwareSessionContext implements SessionContext {
36 31
37 - private volatile TransportProtos.DeviceInfoProto deviceInfo; 32 + @Getter
  33 + private volatile DeviceId deviceId;
  34 + private volatile DeviceInfoProto deviceInfo;
38 35
39 public long getDeviceIdMSB() { 36 public long getDeviceIdMSB() {
40 return deviceInfo.getDeviceIdMSB(); 37 return deviceInfo.getDeviceIdMSB();
@@ -44,6 +41,16 @@ public abstract class DeviceAwareSessionContext implements SessionContext { @@ -44,6 +41,16 @@ public abstract class DeviceAwareSessionContext implements SessionContext {
44 return deviceInfo.getDeviceIdLSB(); 41 return deviceInfo.getDeviceIdLSB();
45 } 42 }
46 43
  44 + public DeviceId getDeviceId() {
  45 + return deviceId;
  46 + }
  47 +
  48 + public void setDeviceInfo(DeviceInfoProto deviceInfo) {
  49 + this.deviceInfo = deviceInfo;
  50 + this.deviceId = new DeviceId(new UUID(deviceInfo.getDeviceIdMSB(), deviceInfo.getDeviceIdLSB()));
  51 + }
  52 +
  53 +
47 public boolean isConnected() { 54 public boolean isConnected() {
48 return deviceInfo != null; 55 return deviceInfo != null;
49 } 56 }
@@ -23,9 +23,12 @@ option java_outer_classname = "TransportProtos"; @@ -23,9 +23,12 @@ option java_outer_classname = "TransportProtos";
23 * Data Structures; 23 * Data Structures;
24 */ 24 */
25 message SessionInfoProto { 25 message SessionInfoProto {
26 - string nodeId = 1;  
27 - int64 sessionIdMSB = 2;  
28 - int64 sessionIdLSB = 3; 26 + int64 sessionIdMSB = 1;
  27 + int64 sessionIdLSB = 2;
  28 + int64 tenantIdMSB = 3;
  29 + int64 tenantIdLSB = 4;
  30 + int64 deviceIdMSB = 5;
  31 + int64 deviceIdLSB = 6;
29 } 32 }
30 33
31 enum SessionEvent { 34 enum SessionEvent {
@@ -33,12 +36,25 @@ enum SessionEvent { @@ -33,12 +36,25 @@ enum SessionEvent {
33 CLOSED = 1; 36 CLOSED = 1;
34 } 37 }
35 38
  39 +enum SessionType {
  40 + SYNC = 0;
  41 + ASYNC = 1;
  42 +}
  43 +
  44 +enum KeyValueType {
  45 + BOOLEAN_V = 0;
  46 + LONG_V = 1;
  47 + DOUBLE_V = 2;
  48 + STRING_V = 3;
  49 +}
  50 +
36 message KeyValueProto { 51 message KeyValueProto {
37 string key = 1; 52 string key = 1;
38 - bool bool_v = 2;  
39 - int64 long_v = 3;  
40 - double double_v = 4;  
41 - string string_v = 5; 53 + KeyValueType type = 2;
  54 + bool bool_v = 3;
  55 + int64 long_v = 4;
  56 + double double_v = 5;
  57 + string string_v = 6;
42 } 58 }
43 59
44 message TsKvListProto { 60 message TsKvListProto {
@@ -60,33 +76,28 @@ message DeviceInfoProto { @@ -60,33 +76,28 @@ message DeviceInfoProto {
60 * Messages that use Data Structures; 76 * Messages that use Data Structures;
61 */ 77 */
62 message SessionEventMsg { 78 message SessionEventMsg {
63 - SessionInfoProto sessionInfo = 1;  
64 - int64 deviceIdMSB = 2;  
65 - int64 deviceIdLSB = 3;  
66 - SessionEvent event = 4; 79 + string nodeId = 1;
  80 + SessionType sessionType = 2;
  81 + SessionEvent event = 3;
67 } 82 }
68 83
69 message PostTelemetryMsg { 84 message PostTelemetryMsg {
70 - SessionInfoProto sessionInfo = 1;  
71 - repeated TsKvListProto tsKvList = 2; 85 + repeated TsKvListProto tsKvList = 1;
72 } 86 }
73 87
74 message PostAttributeMsg { 88 message PostAttributeMsg {
75 - SessionInfoProto sessionInfo = 1;  
76 - repeated TsKvListProto tsKvList = 2; 89 + repeated KeyValueProto kv = 1;
77 } 90 }
78 91
79 message GetAttributeRequestMsg { 92 message GetAttributeRequestMsg {
80 - SessionInfoProto sessionInfo = 1;  
81 - repeated string clientAttributeNames = 2;  
82 - repeated string sharedAttributeNames = 3; 93 + repeated string clientAttributeNames = 1;
  94 + repeated string sharedAttributeNames = 2;
83 } 95 }
84 96
85 message GetAttributeResponseMsg { 97 message GetAttributeResponseMsg {
86 - SessionInfoProto sessionInfo = 1;  
87 - repeated TsKvListProto clientAttributeList = 2;  
88 - repeated TsKvListProto sharedAttributeList = 3;  
89 - repeated string deletedAttributeKeys = 4; 98 + repeated TsKvListProto clientAttributeList = 1;
  99 + repeated TsKvListProto sharedAttributeList = 2;
  100 + repeated string deletedAttributeKeys = 3;
90 } 101 }
91 102
92 message ValidateDeviceTokenRequestMsg { 103 message ValidateDeviceTokenRequestMsg {
@@ -101,11 +112,34 @@ message ValidateDeviceCredentialsResponseMsg { @@ -101,11 +112,34 @@ message ValidateDeviceCredentialsResponseMsg {
101 DeviceInfoProto deviceInfo = 1; 112 DeviceInfoProto deviceInfo = 1;
102 } 113 }
103 114
  115 +message SessionCloseNotificationProto {
  116 + string message = 1;
  117 +}
  118 +
  119 +message TransportToDeviceActorMsg {
  120 + SessionInfoProto sessionInfo = 1;
  121 + SessionEventMsg sessionEvent = 2;
  122 + PostTelemetryMsg postTelemetry = 3;
  123 + PostAttributeMsg postAttributes = 4;
  124 + GetAttributeRequestMsg getAttributes = 5;
  125 +}
  126 +
  127 +message DeviceActorToTransportMsg {
  128 + int64 sessionIdMSB = 1;
  129 + int64 sessionIdLSB = 2;
  130 + SessionCloseNotificationProto sessionCloseNotification = 3;
  131 + GetAttributeResponseMsg getAttributesResponse = 4;
  132 +}
  133 +
104 /** 134 /**
105 * Main messages; 135 * Main messages;
106 */ 136 */
107 -message TransportToRuleEngineMsg { 137 +message ToRuleEngineMsg {
  138 + TransportToDeviceActorMsg toDeviceActorMsg = 1;
  139 +}
108 140
  141 +message ToTransportMsg {
  142 + DeviceActorToTransportMsg toDeviceSessionMsg = 1;
109 } 143 }
110 144
111 message TransportApiRequestMsg { 145 message TransportApiRequestMsg {
1 /** 1 /**
2 * Copyright © 2016-2018 The Thingsboard Authors 2 * Copyright © 2016-2018 The Thingsboard Authors
3 - * 3 + * <p>
4 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License. 5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at 6 * You may obtain a copy of the License at
7 - *  
8 - * http://www.apache.org/licenses/LICENSE-2.0  
9 - * 7 + * <p>
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + * <p>
10 * Unless required by applicable law or agreed to in writing, software 10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS, 11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -26,32 +26,25 @@ import io.netty.handler.codec.mqtt.MqttFixedHeader; @@ -26,32 +26,25 @@ import io.netty.handler.codec.mqtt.MqttFixedHeader;
26 import io.netty.handler.codec.mqtt.MqttMessage; 26 import io.netty.handler.codec.mqtt.MqttMessage;
27 import io.netty.handler.codec.mqtt.MqttMessageIdVariableHeader; 27 import io.netty.handler.codec.mqtt.MqttMessageIdVariableHeader;
28 import io.netty.handler.codec.mqtt.MqttPubAckMessage; 28 import io.netty.handler.codec.mqtt.MqttPubAckMessage;
29 -import io.netty.handler.codec.mqtt.MqttPublishMessage;  
30 import io.netty.handler.codec.mqtt.MqttQoS; 29 import io.netty.handler.codec.mqtt.MqttQoS;
31 import io.netty.handler.codec.mqtt.MqttSubAckMessage; 30 import io.netty.handler.codec.mqtt.MqttSubAckMessage;
32 import io.netty.handler.codec.mqtt.MqttSubAckPayload; 31 import io.netty.handler.codec.mqtt.MqttSubAckPayload;
33 -import io.netty.handler.codec.mqtt.MqttSubscribeMessage;  
34 -import io.netty.handler.codec.mqtt.MqttTopicSubscription;  
35 -import io.netty.handler.codec.mqtt.MqttUnsubscribeMessage;  
36 import io.netty.handler.ssl.SslHandler; 32 import io.netty.handler.ssl.SslHandler;
37 import io.netty.util.concurrent.Future; 33 import io.netty.util.concurrent.Future;
38 import io.netty.util.concurrent.GenericFutureListener; 34 import io.netty.util.concurrent.GenericFutureListener;
39 import lombok.extern.slf4j.Slf4j; 35 import lombok.extern.slf4j.Slf4j;
40 import org.springframework.util.StringUtils; 36 import org.springframework.util.StringUtils;
41 -import org.thingsboard.server.common.data.Device;  
42 -import org.thingsboard.server.common.data.security.DeviceTokenCredentials;  
43 -import org.thingsboard.server.common.data.security.DeviceX509Credentials;  
44 -import org.thingsboard.server.common.msg.core.SessionOpenMsg;  
45 -import org.thingsboard.server.common.msg.session.AdaptorToSessionActorMsg;  
46 -import org.thingsboard.server.common.msg.session.BasicAdaptorToSessionActorMsg;  
47 -import org.thingsboard.server.common.msg.session.BasicTransportToDeviceSessionActorMsg;  
48 -import org.thingsboard.server.common.msg.session.ctrl.SessionCloseMsg;  
49 import org.thingsboard.server.common.transport.TransportService; 37 import org.thingsboard.server.common.transport.TransportService;
50 import org.thingsboard.server.common.transport.TransportServiceCallback; 38 import org.thingsboard.server.common.transport.TransportServiceCallback;
51 -import org.thingsboard.server.common.transport.adaptor.AdaptorException;  
52 import org.thingsboard.server.common.transport.quota.QuotaService; 39 import org.thingsboard.server.common.transport.quota.QuotaService;
53 import org.thingsboard.server.dao.EncryptionUtil; 40 import org.thingsboard.server.dao.EncryptionUtil;
54 -import org.thingsboard.server.gen.transport.TransportProtos; 41 +import org.thingsboard.server.gen.transport.TransportProtos.DeviceInfoProto;
  42 +import org.thingsboard.server.gen.transport.TransportProtos.SessionEvent;
  43 +import org.thingsboard.server.gen.transport.TransportProtos.SessionEventMsg;
  44 +import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto;
  45 +import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceCredentialsResponseMsg;
  46 +import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceTokenRequestMsg;
  47 +import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceX509CertRequestMsg;
55 import org.thingsboard.server.transport.mqtt.adaptors.MqttTransportAdaptor; 48 import org.thingsboard.server.transport.mqtt.adaptors.MqttTransportAdaptor;
56 import org.thingsboard.server.transport.mqtt.session.DeviceSessionCtx; 49 import org.thingsboard.server.transport.mqtt.session.DeviceSessionCtx;
57 import org.thingsboard.server.transport.mqtt.session.GatewaySessionCtx; 50 import org.thingsboard.server.transport.mqtt.session.GatewaySessionCtx;
@@ -61,49 +54,19 @@ import javax.net.ssl.SSLPeerUnverifiedException; @@ -61,49 +54,19 @@ import javax.net.ssl.SSLPeerUnverifiedException;
61 import javax.security.cert.X509Certificate; 54 import javax.security.cert.X509Certificate;
62 import java.io.IOException; 55 import java.io.IOException;
63 import java.net.InetSocketAddress; 56 import java.net.InetSocketAddress;
64 -import java.util.ArrayList;  
65 import java.util.List; 57 import java.util.List;
66 import java.util.UUID; 58 import java.util.UUID;
67 import java.util.concurrent.ConcurrentHashMap; 59 import java.util.concurrent.ConcurrentHashMap;
68 import java.util.concurrent.ConcurrentMap; 60 import java.util.concurrent.ConcurrentMap;
69 61
70 -import org.thingsboard.server.gen.transport.TransportProtos.*;  
71 -  
72 import static io.netty.handler.codec.mqtt.MqttConnectReturnCode.CONNECTION_ACCEPTED; 62 import static io.netty.handler.codec.mqtt.MqttConnectReturnCode.CONNECTION_ACCEPTED;
73 import static io.netty.handler.codec.mqtt.MqttConnectReturnCode.CONNECTION_REFUSED_BAD_USER_NAME_OR_PASSWORD; 63 import static io.netty.handler.codec.mqtt.MqttConnectReturnCode.CONNECTION_REFUSED_BAD_USER_NAME_OR_PASSWORD;
74 import static io.netty.handler.codec.mqtt.MqttConnectReturnCode.CONNECTION_REFUSED_NOT_AUTHORIZED; 64 import static io.netty.handler.codec.mqtt.MqttConnectReturnCode.CONNECTION_REFUSED_NOT_AUTHORIZED;
75 import static io.netty.handler.codec.mqtt.MqttMessageType.CONNACK; 65 import static io.netty.handler.codec.mqtt.MqttMessageType.CONNACK;
76 -import static io.netty.handler.codec.mqtt.MqttMessageType.PINGRESP;  
77 import static io.netty.handler.codec.mqtt.MqttMessageType.PUBACK; 66 import static io.netty.handler.codec.mqtt.MqttMessageType.PUBACK;
78 import static io.netty.handler.codec.mqtt.MqttMessageType.SUBACK; 67 import static io.netty.handler.codec.mqtt.MqttMessageType.SUBACK;
79 -import static io.netty.handler.codec.mqtt.MqttMessageType.UNSUBACK;  
80 import static io.netty.handler.codec.mqtt.MqttQoS.AT_LEAST_ONCE; 68 import static io.netty.handler.codec.mqtt.MqttQoS.AT_LEAST_ONCE;
81 import static io.netty.handler.codec.mqtt.MqttQoS.AT_MOST_ONCE; 69 import static io.netty.handler.codec.mqtt.MqttQoS.AT_MOST_ONCE;
82 -import static io.netty.handler.codec.mqtt.MqttQoS.FAILURE;  
83 -import static org.thingsboard.server.common.msg.session.SessionMsgType.GET_ATTRIBUTES_REQUEST;  
84 -import static org.thingsboard.server.common.msg.session.SessionMsgType.POST_ATTRIBUTES_REQUEST;  
85 -import static org.thingsboard.server.common.msg.session.SessionMsgType.POST_TELEMETRY_REQUEST;  
86 -import static org.thingsboard.server.common.msg.session.SessionMsgType.SUBSCRIBE_ATTRIBUTES_REQUEST;  
87 -import static org.thingsboard.server.common.msg.session.SessionMsgType.SUBSCRIBE_RPC_COMMANDS_REQUEST;  
88 -import static org.thingsboard.server.common.msg.session.SessionMsgType.TO_DEVICE_RPC_RESPONSE;  
89 -import static org.thingsboard.server.common.msg.session.SessionMsgType.TO_SERVER_RPC_REQUEST;  
90 -import static org.thingsboard.server.common.msg.session.SessionMsgType.UNSUBSCRIBE_ATTRIBUTES_REQUEST;  
91 -import static org.thingsboard.server.common.msg.session.SessionMsgType.UNSUBSCRIBE_RPC_COMMANDS_REQUEST;  
92 -import static org.thingsboard.server.transport.mqtt.MqttTopics.BASE_GATEWAY_API_TOPIC;  
93 -import static org.thingsboard.server.transport.mqtt.MqttTopics.DEVICE_ATTRIBUTES_REQUEST_TOPIC_PREFIX;  
94 -import static org.thingsboard.server.transport.mqtt.MqttTopics.DEVICE_ATTRIBUTES_RESPONSES_TOPIC;  
95 -import static org.thingsboard.server.transport.mqtt.MqttTopics.DEVICE_ATTRIBUTES_TOPIC;  
96 -import static org.thingsboard.server.transport.mqtt.MqttTopics.DEVICE_RPC_REQUESTS_SUB_TOPIC;  
97 -import static org.thingsboard.server.transport.mqtt.MqttTopics.DEVICE_RPC_REQUESTS_TOPIC;  
98 -import static org.thingsboard.server.transport.mqtt.MqttTopics.DEVICE_RPC_RESPONSE_SUB_TOPIC;  
99 -import static org.thingsboard.server.transport.mqtt.MqttTopics.DEVICE_RPC_RESPONSE_TOPIC;  
100 -import static org.thingsboard.server.transport.mqtt.MqttTopics.DEVICE_TELEMETRY_TOPIC;  
101 -import static org.thingsboard.server.transport.mqtt.MqttTopics.GATEWAY_ATTRIBUTES_REQUEST_TOPIC;  
102 -import static org.thingsboard.server.transport.mqtt.MqttTopics.GATEWAY_ATTRIBUTES_TOPIC;  
103 -import static org.thingsboard.server.transport.mqtt.MqttTopics.GATEWAY_CONNECT_TOPIC;  
104 -import static org.thingsboard.server.transport.mqtt.MqttTopics.GATEWAY_DISCONNECT_TOPIC;  
105 -import static org.thingsboard.server.transport.mqtt.MqttTopics.GATEWAY_RPC_TOPIC;  
106 -import static org.thingsboard.server.transport.mqtt.MqttTopics.GATEWAY_TELEMETRY_TOPIC;  
107 70
108 /** 71 /**
109 * @author Andrew Shvayka 72 * @author Andrew Shvayka
@@ -389,7 +352,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement @@ -389,7 +352,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
389 } else { 352 } else {
390 ctx.writeAndFlush(createMqttConnAckMsg(CONNECTION_ACCEPTED)); 353 ctx.writeAndFlush(createMqttConnAckMsg(CONNECTION_ACCEPTED));
391 deviceSessionCtx.setDeviceInfo(msg.getDeviceInfo()); 354 deviceSessionCtx.setDeviceInfo(msg.getDeviceInfo());
392 - transportService.process(getSessionEventMsg(SessionEvent.OPEN), null); 355 + transportService.process(deviceSessionCtx, getSessionEventMsg(SessionEvent.OPEN), null);
393 checkGatewaySession(); 356 checkGatewaySession();
394 } 357 }
395 } 358 }
@@ -418,7 +381,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement @@ -418,7 +381,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
418 } else { 381 } else {
419 ctx.writeAndFlush(createMqttConnAckMsg(CONNECTION_ACCEPTED)); 382 ctx.writeAndFlush(createMqttConnAckMsg(CONNECTION_ACCEPTED));
420 deviceSessionCtx.setDeviceInfo(msg.getDeviceInfo()); 383 deviceSessionCtx.setDeviceInfo(msg.getDeviceInfo());
421 - transportService.process(getSessionEventMsg(SessionEvent.OPEN), null); 384 + transportService.process(deviceSessionCtx, getSessionEventMsg(SessionEvent.OPEN), null);
422 checkGatewaySession(); 385 checkGatewaySession();
423 } 386 }
424 } 387 }
@@ -452,7 +415,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement @@ -452,7 +415,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
452 private void processDisconnect(ChannelHandlerContext ctx) { 415 private void processDisconnect(ChannelHandlerContext ctx) {
453 ctx.close(); 416 ctx.close();
454 if (deviceSessionCtx.isConnected()) { 417 if (deviceSessionCtx.isConnected()) {
455 - transportService.process(getSessionEventMsg(SessionEvent.CLOSED), null); 418 + transportService.process(deviceSessionCtx, getSessionEventMsg(SessionEvent.CLOSED), null);
456 if (gatewaySessionCtx != null) { 419 if (gatewaySessionCtx != null) {
457 gatewaySessionCtx.onGatewayDisconnect(); 420 gatewaySessionCtx.onGatewayDisconnect();
458 } 421 }
@@ -534,7 +497,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement @@ -534,7 +497,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
534 @Override 497 @Override
535 public void operationComplete(Future<? super Void> future) throws Exception { 498 public void operationComplete(Future<? super Void> future) throws Exception {
536 if (deviceSessionCtx.isConnected()) { 499 if (deviceSessionCtx.isConnected()) {
537 - transportService.process(getSessionEventMsg(SessionEvent.CLOSED), null); 500 + transportService.process(deviceSessionCtx, getSessionEventMsg(SessionEvent.CLOSED), null);
538 } 501 }
539 } 502 }
540 } 503 }
1 /** 1 /**
2 * Copyright © 2016-2018 The Thingsboard Authors 2 * Copyright © 2016-2018 The Thingsboard Authors
3 - * 3 + * <p>
4 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License. 5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at 6 * You may obtain a copy of the License at
7 - *  
8 - * http://www.apache.org/licenses/LICENSE-2.0  
9 - * 7 + * <p>
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + * <p>
10 * Unless required by applicable law or agreed to in writing, software 10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS, 11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -15,21 +15,41 @@ @@ -15,21 +15,41 @@
15 */ 15 */
16 package org.thingsboard.server.mqtt.service; 16 package org.thingsboard.server.mqtt.service;
17 17
  18 +import lombok.extern.slf4j.Slf4j;
  19 +import org.apache.kafka.clients.consumer.ConsumerRecords;
  20 +import org.apache.kafka.clients.producer.Callback;
  21 +import org.apache.kafka.clients.producer.RecordMetadata;
18 import org.springframework.beans.factory.annotation.Autowired; 22 import org.springframework.beans.factory.annotation.Autowired;
19 import org.springframework.beans.factory.annotation.Value; 23 import org.springframework.beans.factory.annotation.Value;
20 import org.springframework.stereotype.Service; 24 import org.springframework.stereotype.Service;
  25 +import org.thingsboard.server.common.transport.SessionMsgListener;
21 import org.thingsboard.server.common.transport.TransportService; 26 import org.thingsboard.server.common.transport.TransportService;
22 import org.thingsboard.server.common.transport.TransportServiceCallback; 27 import org.thingsboard.server.common.transport.TransportServiceCallback;
  28 +import org.thingsboard.server.gen.transport.TransportProtos;
  29 +import org.thingsboard.server.gen.transport.TransportProtos.PostAttributeMsg;
  30 +import org.thingsboard.server.gen.transport.TransportProtos.PostTelemetryMsg;
  31 +import org.thingsboard.server.gen.transport.TransportProtos.SessionEventMsg;
  32 +import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto;
  33 +import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg;
  34 +import org.thingsboard.server.gen.transport.TransportProtos.TransportApiRequestMsg;
  35 +import org.thingsboard.server.gen.transport.TransportProtos.TransportApiResponseMsg;
  36 +import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg;
  37 +import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceCredentialsResponseMsg;
  38 +import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceTokenRequestMsg;
  39 +import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceX509CertRequestMsg;
23 import org.thingsboard.server.kafka.AsyncCallbackTemplate; 40 import org.thingsboard.server.kafka.AsyncCallbackTemplate;
24 import org.thingsboard.server.kafka.TBKafkaConsumerTemplate; 41 import org.thingsboard.server.kafka.TBKafkaConsumerTemplate;
25 import org.thingsboard.server.kafka.TBKafkaProducerTemplate; 42 import org.thingsboard.server.kafka.TBKafkaProducerTemplate;
26 import org.thingsboard.server.kafka.TbKafkaRequestTemplate; 43 import org.thingsboard.server.kafka.TbKafkaRequestTemplate;
27 -import org.thingsboard.server.gen.transport.TransportProtos.*;  
28 import org.thingsboard.server.kafka.TbKafkaSettings; 44 import org.thingsboard.server.kafka.TbKafkaSettings;
29 import org.thingsboard.server.transport.mqtt.MqttTransportContext; 45 import org.thingsboard.server.transport.mqtt.MqttTransportContext;
30 46
31 import javax.annotation.PostConstruct; 47 import javax.annotation.PostConstruct;
32 import javax.annotation.PreDestroy; 48 import javax.annotation.PreDestroy;
  49 +import java.time.Duration;
  50 +import java.util.UUID;
  51 +import java.util.concurrent.ConcurrentHashMap;
  52 +import java.util.concurrent.ConcurrentMap;
33 import java.util.concurrent.ExecutorService; 53 import java.util.concurrent.ExecutorService;
34 import java.util.concurrent.Executors; 54 import java.util.concurrent.Executors;
35 55
@@ -37,10 +57,17 @@ import java.util.concurrent.Executors; @@ -37,10 +57,17 @@ import java.util.concurrent.Executors;
37 * Created by ashvayka on 05.10.18. 57 * Created by ashvayka on 05.10.18.
38 */ 58 */
39 @Service 59 @Service
  60 +@Slf4j
40 public class MqttTransportService implements TransportService { 61 public class MqttTransportService implements TransportService {
41 62
42 @Value("${kafka.rule_engine.topic}") 63 @Value("${kafka.rule_engine.topic}")
43 private String ruleEngineTopic; 64 private String ruleEngineTopic;
  65 + @Value("${kafka.notifications.topic}")
  66 + private String notificationsTopic;
  67 + @Value("${kafka.notifications.poll_interval}")
  68 + private int notificationsPollDuration;
  69 + @Value("${kafka.notifications.auto_commit_interval}")
  70 + private int notificationsAutoCommitInterval;
44 @Value("${kafka.transport_api.requests_topic}") 71 @Value("${kafka.transport_api.requests_topic}")
45 private String transportApiRequestsTopic; 72 private String transportApiRequestsTopic;
46 @Value("${kafka.transport_api.responses_topic}") 73 @Value("${kafka.transport_api.responses_topic}")
@@ -54,6 +81,8 @@ public class MqttTransportService implements TransportService { @@ -54,6 +81,8 @@ public class MqttTransportService implements TransportService {
54 @Value("${kafka.transport_api.response_auto_commit_interval}") 81 @Value("${kafka.transport_api.response_auto_commit_interval}")
55 private int autoCommitInterval; 82 private int autoCommitInterval;
56 83
  84 + private ConcurrentMap<UUID, SessionMsgListener> sessions = new ConcurrentHashMap<>();
  85 +
57 @Autowired 86 @Autowired
58 private TbKafkaSettings kafkaSettings; 87 private TbKafkaSettings kafkaSettings;
59 //We use this to get the node id. We should replace this with a component that provides the node id. 88 //We use this to get the node id. We should replace this with a component that provides the node id.
@@ -63,6 +92,12 @@ public class MqttTransportService implements TransportService { @@ -63,6 +92,12 @@ public class MqttTransportService implements TransportService {
63 private ExecutorService transportCallbackExecutor; 92 private ExecutorService transportCallbackExecutor;
64 93
65 private TbKafkaRequestTemplate<TransportApiRequestMsg, TransportApiResponseMsg> transportApiTemplate; 94 private TbKafkaRequestTemplate<TransportApiRequestMsg, TransportApiResponseMsg> transportApiTemplate;
  95 + private TBKafkaProducerTemplate<ToRuleEngineMsg> ruleEngineProducer;
  96 + private TBKafkaConsumerTemplate<ToTransportMsg> mainConsumer;
  97 +
  98 + private ExecutorService mainConsumerExecutor = Executors.newSingleThreadExecutor();
  99 +
  100 + private volatile boolean stopped = false;
66 101
67 @PostConstruct 102 @PostConstruct
68 public void init() { 103 public void init() {
@@ -77,7 +112,7 @@ public class MqttTransportService implements TransportService { @@ -77,7 +112,7 @@ public class MqttTransportService implements TransportService {
77 responseBuilder.settings(kafkaSettings); 112 responseBuilder.settings(kafkaSettings);
78 responseBuilder.topic(transportApiResponsesTopic + "." + transportContext.getNodeId()); 113 responseBuilder.topic(transportApiResponsesTopic + "." + transportContext.getNodeId());
79 responseBuilder.clientId(transportContext.getNodeId()); 114 responseBuilder.clientId(transportContext.getNodeId());
80 - responseBuilder.groupId("transport-node"); 115 + responseBuilder.groupId(null);
81 responseBuilder.autoCommit(true); 116 responseBuilder.autoCommit(true);
82 responseBuilder.autoCommitIntervalMs(autoCommitInterval); 117 responseBuilder.autoCommitIntervalMs(autoCommitInterval);
83 responseBuilder.decoder(new TransportApiResponseDecoder()); 118 responseBuilder.decoder(new TransportApiResponseDecoder());
@@ -91,16 +126,79 @@ public class MqttTransportService implements TransportService { @@ -91,16 +126,79 @@ public class MqttTransportService implements TransportService {
91 builder.pollInterval(responsePollDuration); 126 builder.pollInterval(responsePollDuration);
92 transportApiTemplate = builder.build(); 127 transportApiTemplate = builder.build();
93 transportApiTemplate.init(); 128 transportApiTemplate.init();
  129 +
  130 + TBKafkaProducerTemplate.TBKafkaProducerTemplateBuilder<ToRuleEngineMsg> ruleEngineProducerBuilder = TBKafkaProducerTemplate.builder();
  131 + ruleEngineProducerBuilder.settings(kafkaSettings);
  132 + ruleEngineProducerBuilder.defaultTopic(ruleEngineTopic);
  133 + ruleEngineProducerBuilder.encoder(new ToRuleEngineMsgEncoder());
  134 + ruleEngineProducer = ruleEngineProducerBuilder.build();
  135 + ruleEngineProducer.init();
  136 +
  137 + TBKafkaConsumerTemplate.TBKafkaConsumerTemplateBuilder<ToTransportMsg> mainConsumerBuilder = TBKafkaConsumerTemplate.builder();
  138 + mainConsumerBuilder.settings(kafkaSettings);
  139 + mainConsumerBuilder.topic(notificationsTopic + "." + transportContext.getNodeId());
  140 + mainConsumerBuilder.clientId(transportContext.getNodeId());
  141 + mainConsumerBuilder.groupId(null);
  142 + mainConsumerBuilder.autoCommit(true);
  143 + mainConsumerBuilder.autoCommitIntervalMs(notificationsAutoCommitInterval);
  144 + mainConsumerBuilder.decoder(new ToTransportMsgResponseDecoder());
  145 + mainConsumer = mainConsumerBuilder.build();
  146 + mainConsumer.subscribe();
  147 +
  148 + mainConsumerExecutor.execute(() -> {
  149 + while (!stopped) {
  150 + try {
  151 + ConsumerRecords<String, byte[]> records = mainConsumer.poll(Duration.ofMillis(notificationsPollDuration));
  152 + records.forEach(record -> {
  153 + try {
  154 + ToTransportMsg toTransportMsg = mainConsumer.decode(record);
  155 + if (toTransportMsg.hasToDeviceSessionMsg()) {
  156 + TransportProtos.DeviceActorToTransportMsg toSessionMsg = toTransportMsg.getToDeviceSessionMsg();
  157 + UUID sessionId = new UUID(toSessionMsg.getSessionIdMSB(), toSessionMsg.getSessionIdLSB());
  158 + SessionMsgListener listener = sessions.get(sessionId);
  159 + if (listener != null) {
  160 + transportCallbackExecutor.submit(() -> {
  161 + if (toSessionMsg.hasGetAttributesResponse()) {
  162 + listener.onGetAttributesResponse(toSessionMsg.getGetAttributesResponse());
  163 + }
  164 + });
  165 + } else {
  166 + //TODO: should we notify the device actor about missed session?
  167 + log.debug("[{}] Missing session.", sessionId);
  168 + }
  169 +
  170 + }
  171 + } catch (Throwable e) {
  172 + log.warn("Failed to process the notification.", e);
  173 + }
  174 + });
  175 + } catch (Exception e) {
  176 + log.warn("Failed to obtain messages from queue.", e);
  177 + try {
  178 + Thread.sleep(notificationsPollDuration);
  179 + } catch (InterruptedException e2) {
  180 + log.trace("Failed to wait until the server has capacity to handle new requests", e2);
  181 + }
  182 + }
  183 + }
  184 + });
94 } 185 }
95 186
96 @PreDestroy 187 @PreDestroy
97 public void destroy() { 188 public void destroy() {
  189 + stopped = true;
98 if (transportApiTemplate != null) { 190 if (transportApiTemplate != null) {
99 transportApiTemplate.stop(); 191 transportApiTemplate.stop();
100 } 192 }
101 if (transportCallbackExecutor != null) { 193 if (transportCallbackExecutor != null) {
102 transportCallbackExecutor.shutdownNow(); 194 transportCallbackExecutor.shutdownNow();
103 } 195 }
  196 + if (mainConsumer != null) {
  197 + mainConsumer.unsubscribe();
  198 + }
  199 + if (mainConsumerExecutor != null) {
  200 + mainConsumerExecutor.shutdownNow();
  201 + }
104 } 202 }
105 203
106 @Override 204 @Override
@@ -118,17 +216,69 @@ public class MqttTransportService implements TransportService { @@ -118,17 +216,69 @@ public class MqttTransportService implements TransportService {
118 } 216 }
119 217
120 @Override 218 @Override
121 - public void process(SessionEventMsg msg, TransportServiceCallback<Void> callback) { 219 + public void process(SessionInfoProto sessionInfo, SessionEventMsg msg, TransportServiceCallback<Void> callback) {
  220 + ToRuleEngineMsg toRuleEngineMsg = ToRuleEngineMsg.newBuilder().setToDeviceActorMsg(
  221 + TransportProtos.TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo)
  222 + .setSessionEvent(msg).build()
  223 + ).build();
  224 + send(sessionInfo, toRuleEngineMsg, callback);
  225 + }
122 226
  227 + @Override
  228 + public void process(SessionInfoProto sessionInfo, PostTelemetryMsg msg, TransportServiceCallback<Void> callback) {
  229 + ToRuleEngineMsg toRuleEngineMsg = ToRuleEngineMsg.newBuilder().setToDeviceActorMsg(
  230 + TransportProtos.TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo)
  231 + .setPostTelemetry(msg).build()
  232 + ).build();
  233 + send(sessionInfo, toRuleEngineMsg, callback);
123 } 234 }
124 235
125 @Override 236 @Override
126 - public void process(PostTelemetryMsg msg, TransportServiceCallback<Void> callback) { 237 + public void process(SessionInfoProto sessionInfo, PostAttributeMsg msg, TransportServiceCallback<Void> callback) {
  238 + ToRuleEngineMsg toRuleEngineMsg = ToRuleEngineMsg.newBuilder().setToDeviceActorMsg(
  239 + TransportProtos.TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo)
  240 + .setPostAttributes(msg).build()
  241 + ).build();
  242 + send(sessionInfo, toRuleEngineMsg, callback);
  243 + }
127 244
  245 + @Override
  246 + public void registerSession(SessionInfoProto sessionInfo, SessionMsgListener listener) {
  247 + sessions.putIfAbsent(toId(sessionInfo), listener);
  248 + //TODO: monitor sessions periodically: PING REQ/RESP, etc.
128 } 249 }
129 250
130 @Override 251 @Override
131 - public void process(PostAttributeMsg msg, TransportServiceCallback<Void> callback) { 252 + public void deregisterSession(SessionInfoProto sessionInfo) {
  253 + sessions.remove(toId(sessionInfo));
  254 + }
  255 +
  256 + private UUID toId(SessionInfoProto sessionInfo) {
  257 + return new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB());
  258 + }
  259 +
  260 + private String getRoutingKey(SessionInfoProto sessionInfo) {
  261 + return new UUID(sessionInfo.getDeviceIdMSB(), sessionInfo.getDeviceIdLSB()).toString();
  262 + }
  263 +
  264 + private static class TransportCallbackAdaptor implements Callback {
  265 + private final TransportServiceCallback<Void> callback;
  266 +
  267 + TransportCallbackAdaptor(TransportServiceCallback<Void> callback) {
  268 + this.callback = callback;
  269 + }
  270 +
  271 + @Override
  272 + public void onCompletion(RecordMetadata metadata, Exception exception) {
  273 + if (exception == null) {
  274 + callback.onSuccess(null);
  275 + } else {
  276 + callback.onError(exception);
  277 + }
  278 + }
  279 + }
132 280
  281 + private void send(SessionInfoProto sessionInfo, ToRuleEngineMsg toRuleEngineMsg, TransportServiceCallback<Void> callback) {
  282 + ruleEngineProducer.send(getRoutingKey(sessionInfo), toRuleEngineMsg, new TransportCallbackAdaptor(callback));
133 } 283 }
134 } 284 }
  1 +/**
  2 + * Copyright © 2016-2018 The Thingsboard Authors
  3 + * <p>
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + * <p>
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + * <p>
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.mqtt.service;
  17 +
  18 +import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg;
  19 +import org.thingsboard.server.kafka.TbKafkaEncoder;
  20 +
  21 +/**
  22 + * Created by ashvayka on 05.10.18.
  23 + */
  24 +public class ToRuleEngineMsgEncoder implements TbKafkaEncoder<ToRuleEngineMsg> {
  25 + @Override
  26 + public byte[] encode(ToRuleEngineMsg value) {
  27 + return value.toByteArray();
  28 + }
  29 +}
transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/ToTransportMsgResponseDecoder.java renamed from common/message/src/main/java/org/thingsboard/server/common/msg/device/DeviceToDeviceActorMsg.java
@@ -13,29 +13,19 @@ @@ -13,29 +13,19 @@
13 * See the License for the specific language governing permissions and 13 * See the License for the specific language governing permissions and
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 -package org.thingsboard.server.common.msg.device; 16 +package org.thingsboard.server.mqtt.service;
17 17
18 -import java.io.Serializable;  
19 -import java.util.Optional; 18 +import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg;
  19 +import org.thingsboard.server.kafka.TbKafkaDecoder;
20 20
21 -import org.thingsboard.server.common.data.id.SessionId;  
22 -import org.thingsboard.server.common.msg.TbActorMsg;  
23 -import org.thingsboard.server.common.msg.aware.CustomerAwareMsg;  
24 -import org.thingsboard.server.common.msg.aware.DeviceAwareMsg;  
25 -import org.thingsboard.server.common.msg.aware.TenantAwareMsg;  
26 -import org.thingsboard.server.common.msg.cluster.ServerAddress;  
27 -import org.thingsboard.server.common.msg.session.FromDeviceMsg;  
28 -import org.thingsboard.server.common.msg.session.SessionType; 21 +import java.io.IOException;
29 22
30 -public interface DeviceToDeviceActorMsg extends TbActorMsg, DeviceAwareMsg, CustomerAwareMsg, TenantAwareMsg, Serializable {  
31 -  
32 - SessionId getSessionId();  
33 -  
34 - SessionType getSessionType();  
35 -  
36 - Optional<ServerAddress> getServerAddress();  
37 -  
38 - FromDeviceMsg getPayload();  
39 -  
40 - DeviceToDeviceActorMsg toOtherAddress(ServerAddress otherAddress); 23 +/**
  24 + * Created by ashvayka on 05.10.18.
  25 + */
  26 +public class ToTransportMsgResponseDecoder implements TbKafkaDecoder<ToTransportMsg> {
  27 + @Override
  28 + public ToTransportMsg decode(byte[] data) throws IOException {
  29 + return ToTransportMsg.parseFrom(data);
  30 + }
41 } 31 }
@@ -82,3 +82,7 @@ kafka: @@ -82,3 +82,7 @@ kafka:
82 response_auto_commit_interval: "${TB_TRANSPORT_RESPONSE_AUTO_COMMIT_INTERVAL_MS:100}" 82 response_auto_commit_interval: "${TB_TRANSPORT_RESPONSE_AUTO_COMMIT_INTERVAL_MS:100}"
83 rule_engine: 83 rule_engine:
84 topic: "${TB_RULE_ENGINE_TOPIC:tb.rule-engine}" 84 topic: "${TB_RULE_ENGINE_TOPIC:tb.rule-engine}"
  85 + notifications:
  86 + topic: "${TB_TRANSPORT_NOTIFICATIONS_TOPIC:tb.transport.notifications}"
  87 + poll_interval: "${TB_TRANSPORT_NOTIFICATIONS_POLL_INTERVAL_MS:25}"
  88 + auto_commit_interval: "${TB_TRANSPORT_NOTIFICATIONS_AUTO_COMMIT_INTERVAL_MS:100}"