|
|
|
@ -88,11 +88,11 @@ import java.util.stream.Collectors; |
|
|
|
/** |
|
|
|
* @author Andrew Shvayka |
|
|
|
*/ |
|
|
|
public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { |
|
|
|
class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { |
|
|
|
|
|
|
|
private final TenantId tenantId; |
|
|
|
private final DeviceId deviceId; |
|
|
|
private final Map<UUID, SessionInfo> sessions; |
|
|
|
final TenantId tenantId; |
|
|
|
final DeviceId deviceId; |
|
|
|
private final Map<UUID, SessionInfoMetaData> sessions; |
|
|
|
private final Map<UUID, SessionInfo> attributeSubscriptions; |
|
|
|
private final Map<UUID, SessionInfo> rpcSubscriptions; |
|
|
|
private final Map<Integer, ToDeviceRpcRequestMetadata> toDeviceRpcPendingMap; |
|
|
|
@ -116,6 +116,7 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso |
|
|
|
this.toDeviceRpcPendingMap = new HashMap<>(); |
|
|
|
this.toServerRpcPendingMap = new HashMap<>(); |
|
|
|
initAttributes(); |
|
|
|
restoreSessions(); |
|
|
|
} |
|
|
|
|
|
|
|
private void initAttributes() { |
|
|
|
@ -160,7 +161,6 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso |
|
|
|
} else { |
|
|
|
logger.debug("[{}] RPC request {} is NOT sent!", deviceId, request.getId()); |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
private void registerPendingRpcRequest(ActorContext context, ToDeviceRpcRequestActorMsg msg, boolean sent, ToDeviceRpcRequestMsg rpcRequest, long timeout) { |
|
|
|
@ -174,7 +174,7 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso |
|
|
|
if (requestMd != null) { |
|
|
|
logger.debug("[{}] RPC request [{}] timeout detected!", deviceId, msg.getId()); |
|
|
|
systemContext.getDeviceRpcService().processResponseToServerSideRPCRequestFromDeviceActor(new FromDeviceRpcResponse(requestMd.getMsg().getMsg().getId(), |
|
|
|
null, requestMd.isSent() ? RpcError.TIMEOUT : RpcError.NO_ACTIVE_CONNECTION)); |
|
|
|
null, requestMd.isSent() ? RpcError.TIMEOUT : RpcError.NO_ACTIVE_CONNECTION)); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
@ -227,11 +227,11 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso |
|
|
|
} |
|
|
|
if (msg.hasPostAttributes()) { |
|
|
|
handlePostAttributesRequest(context, msg.getSessionInfo(), msg.getPostAttributes()); |
|
|
|
reportActivity(); |
|
|
|
reportLogicalDeviceActivity(); |
|
|
|
} |
|
|
|
if (msg.hasPostTelemetry()) { |
|
|
|
handlePostTelemetryRequest(context, msg.getSessionInfo(), msg.getPostTelemetry()); |
|
|
|
reportActivity(); |
|
|
|
reportLogicalDeviceActivity(); |
|
|
|
} |
|
|
|
if (msg.hasGetAttributes()) { |
|
|
|
handleGetAttributesRequest(context, msg.getSessionInfo(), msg.getGetAttributes()); |
|
|
|
@ -241,11 +241,14 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso |
|
|
|
} |
|
|
|
if (msg.hasToServerRPCCallRequest()) { |
|
|
|
handleClientSideRPCRequest(context, msg.getSessionInfo(), msg.getToServerRPCCallRequest()); |
|
|
|
reportActivity(); |
|
|
|
reportLogicalDeviceActivity(); |
|
|
|
} |
|
|
|
if (msg.hasSubscriptionInfo()) { |
|
|
|
handleSessionActivity(context, msg.getSessionInfo(), msg.getSubscriptionInfo()); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private void reportActivity() { |
|
|
|
private void reportLogicalDeviceActivity() { |
|
|
|
systemContext.getDeviceStateService().onDeviceActivity(deviceId); |
|
|
|
} |
|
|
|
|
|
|
|
@ -406,28 +409,20 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
void processClusterEventMsg(ClusterEventMsg msg) { |
|
|
|
// if (!msg.isAdded()) {
|
|
|
|
// logger.debug("[{}] Clearing attributes/rpc subscription for server [{}]", deviceId, msg.getServerAddress());
|
|
|
|
// Predicate<Map.Entry<SessionId, SessionInfo>> filter = e -> e.getValue().getServer()
|
|
|
|
// .map(serverAddress -> serverAddress.equals(msg.getServerAddress())).orElse(false);
|
|
|
|
// attributeSubscriptions.entrySet().removeIf(filter);
|
|
|
|
// rpcSubscriptions.entrySet().removeIf(filter);
|
|
|
|
// }
|
|
|
|
} |
|
|
|
|
|
|
|
private void processSubscriptionCommands(ActorContext context, SessionInfoProto sessionInfo, SubscribeToAttributeUpdatesMsg subscribeCmd) { |
|
|
|
UUID sessionId = getSessionId(sessionInfo); |
|
|
|
if (subscribeCmd.getUnsubscribe()) { |
|
|
|
logger.debug("[{}] Canceling attributes subscription for session [{}]", deviceId, sessionId); |
|
|
|
attributeSubscriptions.remove(sessionId); |
|
|
|
} else { |
|
|
|
SessionInfo session = sessions.get(sessionId); |
|
|
|
if (session == null) { |
|
|
|
session = new SessionInfo(TransportProtos.SessionType.SYNC, sessionInfo.getNodeId()); |
|
|
|
SessionInfoMetaData sessionMD = sessions.get(sessionId); |
|
|
|
if (sessionMD == null) { |
|
|
|
sessionMD = new SessionInfoMetaData(new SessionInfo(TransportProtos.SessionType.SYNC, sessionInfo.getNodeId())); |
|
|
|
} |
|
|
|
sessionMD.setSubscribedToAttributes(true); |
|
|
|
logger.debug("[{}] Registering attributes subscription for session [{}]", deviceId, sessionId); |
|
|
|
attributeSubscriptions.put(sessionId, session); |
|
|
|
attributeSubscriptions.put(sessionId, sessionMD.getSessionInfo()); |
|
|
|
dumpSessions(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
@ -441,20 +436,22 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso |
|
|
|
logger.debug("[{}] Canceling rpc subscription for session [{}]", deviceId, sessionId); |
|
|
|
rpcSubscriptions.remove(sessionId); |
|
|
|
} else { |
|
|
|
SessionInfo session = sessions.get(sessionId); |
|
|
|
if (session == null) { |
|
|
|
session = new SessionInfo(TransportProtos.SessionType.SYNC, sessionInfo.getNodeId()); |
|
|
|
SessionInfoMetaData sessionMD = sessions.get(sessionId); |
|
|
|
if (sessionMD == null) { |
|
|
|
sessionMD = new SessionInfoMetaData(new SessionInfo(TransportProtos.SessionType.SYNC, sessionInfo.getNodeId())); |
|
|
|
} |
|
|
|
sessionMD.setSubscribedToRPC(true); |
|
|
|
logger.debug("[{}] Registering rpc subscription for session [{}]", deviceId, sessionId); |
|
|
|
rpcSubscriptions.put(sessionId, session); |
|
|
|
rpcSubscriptions.put(sessionId, sessionMD.getSessionInfo()); |
|
|
|
sendPendingRequests(context, sessionId, sessionInfo); |
|
|
|
dumpSessions(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private void processSessionStateMsgs(SessionInfoProto sessionInfo, SessionEventMsg msg) { |
|
|
|
UUID sessionId = getSessionId(sessionInfo); |
|
|
|
if (msg.getEvent() == SessionEvent.OPEN) { |
|
|
|
if(sessions.containsKey(sessionId)){ |
|
|
|
if (sessions.containsKey(sessionId)) { |
|
|
|
logger.debug("[{}] Received duplicate session open event [{}]", deviceId, sessionId); |
|
|
|
return; |
|
|
|
} |
|
|
|
@ -462,13 +459,14 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso |
|
|
|
if (sessions.size() >= systemContext.getMaxConcurrentSessionsPerDevice()) { |
|
|
|
UUID sessionIdToRemove = sessions.keySet().stream().findFirst().orElse(null); |
|
|
|
if (sessionIdToRemove != null) { |
|
|
|
closeSession(sessionIdToRemove, sessions.remove(sessionIdToRemove)); |
|
|
|
notifyTransportAboutClosedSession(sessionIdToRemove, sessions.remove(sessionIdToRemove)); |
|
|
|
} |
|
|
|
} |
|
|
|
sessions.put(sessionId, new SessionInfo(TransportProtos.SessionType.ASYNC, sessionInfo.getNodeId())); |
|
|
|
sessions.put(sessionId, new SessionInfoMetaData(new SessionInfo(TransportProtos.SessionType.ASYNC, sessionInfo.getNodeId()))); |
|
|
|
if (sessions.size() == 1) { |
|
|
|
reportSessionOpen(); |
|
|
|
} |
|
|
|
dumpSessions(); |
|
|
|
} else if (msg.getEvent() == SessionEvent.CLOSED) { |
|
|
|
logger.debug("[{}] Canceling subscriptions for closed session [{}]", deviceId, sessionId); |
|
|
|
sessions.remove(sessionId); |
|
|
|
@ -477,21 +475,34 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso |
|
|
|
if (sessions.isEmpty()) { |
|
|
|
reportSessionClose(); |
|
|
|
} |
|
|
|
dumpSessions(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private void handleSessionActivity(ActorContext context, SessionInfoProto sessionInfo, TransportProtos.SubscriptionInfoProto subscriptionInfo) { |
|
|
|
UUID sessionId = getSessionId(sessionInfo); |
|
|
|
SessionInfoMetaData sessionMD = sessions.get(sessionId); |
|
|
|
if (sessionMD != null) { |
|
|
|
sessionMD.setLastActivityTime(subscriptionInfo.getLastActivityTime()); |
|
|
|
sessionMD.setSubscribedToAttributes(subscriptionInfo.getAttributeSubscription()); |
|
|
|
sessionMD.setSubscribedToRPC(subscriptionInfo.getRpcSubscription()); |
|
|
|
} |
|
|
|
dumpSessions(); |
|
|
|
} |
|
|
|
|
|
|
|
void processCredentialsUpdate() { |
|
|
|
sessions.forEach(this::closeSession); |
|
|
|
sessions.forEach(this::notifyTransportAboutClosedSession); |
|
|
|
attributeSubscriptions.clear(); |
|
|
|
rpcSubscriptions.clear(); |
|
|
|
dumpSessions(); |
|
|
|
} |
|
|
|
|
|
|
|
private void closeSession(UUID sessionId, SessionInfo sessionInfo) { |
|
|
|
private void notifyTransportAboutClosedSession(UUID sessionId, SessionInfoMetaData sessionMd) { |
|
|
|
DeviceActorToTransportMsg msg = DeviceActorToTransportMsg.newBuilder() |
|
|
|
.setSessionIdMSB(sessionId.getMostSignificantBits()) |
|
|
|
.setSessionIdLSB(sessionId.getLeastSignificantBits()) |
|
|
|
.setSessionCloseNotification(SessionCloseNotificationProto.getDefaultInstance()).build(); |
|
|
|
systemContext.getRuleEngineTransportService().process(sessionInfo.getNodeId(), msg); |
|
|
|
systemContext.getRuleEngineTransportService().process(sessionMd.getSessionInfo().getNodeId(), msg); |
|
|
|
} |
|
|
|
|
|
|
|
void processNameOrTypeUpdate(DeviceNameOrTypeUpdateMsg msg) { |
|
|
|
@ -605,4 +616,67 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso |
|
|
|
} |
|
|
|
return builder.build(); |
|
|
|
} |
|
|
|
|
|
|
|
private void restoreSessions() { |
|
|
|
TransportProtos.DeviceSessionsCacheEntry sessionsDump = systemContext.getDeviceSessionCacheService().get(deviceId); |
|
|
|
if (sessionsDump.getSerializedSize() == 0) { |
|
|
|
return; |
|
|
|
} |
|
|
|
for (TransportProtos.SessionSubscriptionInfoProto sessionSubscriptionInfoProto : sessionsDump.getSessionsList()) { |
|
|
|
SessionInfoProto sessionInfoProto = sessionSubscriptionInfoProto.getSessionInfo(); |
|
|
|
UUID sessionId = getSessionId(sessionInfoProto); |
|
|
|
SessionInfo sessionInfo = new SessionInfo(TransportProtos.SessionType.ASYNC, sessionInfoProto.getNodeId()); |
|
|
|
TransportProtos.SubscriptionInfoProto subInfo = sessionSubscriptionInfoProto.getSubscriptionInfo(); |
|
|
|
SessionInfoMetaData sessionInfoMetaData = new SessionInfoMetaData(sessionInfo, subInfo.getLastActivityTime()); |
|
|
|
sessions.put(sessionId, sessionInfoMetaData); |
|
|
|
if (subInfo.getAttributeSubscription()) { |
|
|
|
rpcSubscriptions.put(sessionId, sessionInfo); |
|
|
|
} |
|
|
|
if (subInfo.getAttributeSubscription()) { |
|
|
|
attributeSubscriptions.put(sessionId, sessionInfo); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private void dumpSessions() { |
|
|
|
List<TransportProtos.SessionSubscriptionInfoProto> sessionsList = new ArrayList<>(sessions.size()); |
|
|
|
sessions.forEach((uuid, sessionMD) -> { |
|
|
|
if (sessionMD.getSessionInfo().getType() == TransportProtos.SessionType.SYNC) { |
|
|
|
return; |
|
|
|
} |
|
|
|
SessionInfo sessionInfo = sessionMD.getSessionInfo(); |
|
|
|
TransportProtos.SubscriptionInfoProto subscriptionInfoProto = TransportProtos.SubscriptionInfoProto.newBuilder() |
|
|
|
.setLastActivityTime(sessionMD.getLastActivityTime()) |
|
|
|
.setAttributeSubscription(sessionMD.isSubscribedToAttributes()) |
|
|
|
.setRpcSubscription(sessionMD.isSubscribedToRPC()).build(); |
|
|
|
TransportProtos.SessionInfoProto sessionInfoProto = TransportProtos.SessionInfoProto.newBuilder() |
|
|
|
.setSessionIdMSB(uuid.getMostSignificantBits()) |
|
|
|
.setSessionIdLSB(uuid.getLeastSignificantBits()) |
|
|
|
.setNodeId(sessionInfo.getNodeId()).build(); |
|
|
|
sessionsList.add(TransportProtos.SessionSubscriptionInfoProto.newBuilder() |
|
|
|
.setSessionInfo(sessionInfoProto) |
|
|
|
.setSubscriptionInfo(subscriptionInfoProto).build()); |
|
|
|
}); |
|
|
|
systemContext.getDeviceSessionCacheService() |
|
|
|
.put(deviceId, TransportProtos.DeviceSessionsCacheEntry.newBuilder() |
|
|
|
.addAllSessions(sessionsList).build()); |
|
|
|
} |
|
|
|
|
|
|
|
void initSessionTimeout(ActorContext context) { |
|
|
|
schedulePeriodicMsgWithDelay(context, SessionTimeoutCheckMsg.instance(), systemContext.getSessionInactivityTimeout(), systemContext.getSessionInactivityTimeout()); |
|
|
|
} |
|
|
|
|
|
|
|
void checkSessionsTimeout() { |
|
|
|
long expTime = System.currentTimeMillis() - systemContext.getSessionInactivityTimeout(); |
|
|
|
Map<UUID, SessionInfoMetaData> sessionsToRemove = sessions.entrySet().stream().filter(kv -> kv.getValue().getLastActivityTime() < expTime).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); |
|
|
|
sessionsToRemove.forEach((sessionId, sessionMD) -> { |
|
|
|
sessions.remove(sessionId); |
|
|
|
rpcSubscriptions.remove(sessionId); |
|
|
|
attributeSubscriptions.remove(sessionId); |
|
|
|
notifyTransportAboutClosedSession(sessionId, sessionMD); |
|
|
|
}); |
|
|
|
if (!sessionsToRemove.isEmpty()) { |
|
|
|
dumpSessions(); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|