Browse Source

WebSocket API for notifications; refactoring

pull/7511/head
ViacheslavKlimov 4 years ago
parent
commit
89d2a337e1
  1. 2
      application/src/main/java/org/thingsboard/server/controller/AbstractRpcController.java
  2. 20
      application/src/main/java/org/thingsboard/server/controller/NotificationController.java
  3. 2
      application/src/main/java/org/thingsboard/server/controller/RpcV2Controller.java
  4. 4
      application/src/main/java/org/thingsboard/server/controller/TelemetryController.java
  5. 54
      application/src/main/java/org/thingsboard/server/controller/plugin/TbWebSocketHandler.java
  6. 2
      application/src/main/java/org/thingsboard/server/exception/AccessDeniedException.java
  7. 2
      application/src/main/java/org/thingsboard/server/exception/EntityNotFoundException.java
  8. 2
      application/src/main/java/org/thingsboard/server/exception/InternalErrorException.java
  9. 2
      application/src/main/java/org/thingsboard/server/exception/InvalidParametersException.java
  10. 2
      application/src/main/java/org/thingsboard/server/exception/ToErrorResponseEntity.java
  11. 2
      application/src/main/java/org/thingsboard/server/exception/UnauthorizedException.java
  12. 2
      application/src/main/java/org/thingsboard/server/exception/UncheckedApiException.java
  13. 58
      application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationProcessingService.java
  14. 6
      application/src/main/java/org/thingsboard/server/service/notification/NotificationProcessingService.java
  15. 25
      application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java
  16. 2
      application/src/main/java/org/thingsboard/server/service/security/AccessValidator.java
  17. 8
      application/src/main/java/org/thingsboard/server/service/security/ValidationCallback.java
  18. 1
      application/src/main/java/org/thingsboard/server/service/security/permission/TenantAdminPermissions.java
  19. 49
      application/src/main/java/org/thingsboard/server/service/subscription/DefaultSubscriptionManagerService.java
  20. 45
      application/src/main/java/org/thingsboard/server/service/subscription/DefaultTbEntityDataSubscriptionService.java
  21. 30
      application/src/main/java/org/thingsboard/server/service/subscription/DefaultTbLocalSubscriptionService.java
  22. 2
      application/src/main/java/org/thingsboard/server/service/subscription/ReadTsKvQueryInfo.java
  23. 2
      application/src/main/java/org/thingsboard/server/service/subscription/SubscriptionErrorCode.java
  24. 6
      application/src/main/java/org/thingsboard/server/service/subscription/SubscriptionManagerService.java
  25. 14
      application/src/main/java/org/thingsboard/server/service/subscription/TbAbstractDataSubCtx.java
  26. 19
      application/src/main/java/org/thingsboard/server/service/subscription/TbAbstractSubCtx.java
  27. 16
      application/src/main/java/org/thingsboard/server/service/subscription/TbAlarmDataSubCtx.java
  28. 9
      application/src/main/java/org/thingsboard/server/service/subscription/TbAlarmsSubscription.java
  29. 6
      application/src/main/java/org/thingsboard/server/service/subscription/TbAttributeSubscription.java
  30. 10
      application/src/main/java/org/thingsboard/server/service/subscription/TbEntityCountSubCtx.java
  31. 18
      application/src/main/java/org/thingsboard/server/service/subscription/TbEntityDataSubCtx.java
  32. 17
      application/src/main/java/org/thingsboard/server/service/subscription/TbEntityDataSubscriptionService.java
  33. 7
      application/src/main/java/org/thingsboard/server/service/subscription/TbLocalSubscriptionService.java
  34. 2
      application/src/main/java/org/thingsboard/server/service/subscription/TbSubscription.java
  35. 2
      application/src/main/java/org/thingsboard/server/service/subscription/TbSubscriptionType.java
  36. 71
      application/src/main/java/org/thingsboard/server/service/subscription/TbSubscriptionUtils.java
  37. 6
      application/src/main/java/org/thingsboard/server/service/subscription/TbTimeseriesSubscription.java
  38. 22
      application/src/main/java/org/thingsboard/server/service/telemetry/AbstractSubscriptionService.java
  39. 32
      application/src/main/java/org/thingsboard/server/service/telemetry/DefaultAlarmSubscriptionService.java
  40. 91
      application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java
  41. 225
      application/src/main/java/org/thingsboard/server/service/telemetry/DefaultWebSocketService.java
  42. 2
      application/src/main/java/org/thingsboard/server/service/telemetry/InternalTelemetryService.java
  43. 2
      application/src/main/java/org/thingsboard/server/service/ws/SessionEvent.java
  44. 11
      application/src/main/java/org/thingsboard/server/service/ws/WebSocketMsgEndpoint.java
  45. 30
      application/src/main/java/org/thingsboard/server/service/ws/WebSocketSessionRef.java
  46. 38
      application/src/main/java/org/thingsboard/server/service/ws/WebSocketSessionType.java
  47. 10
      application/src/main/java/org/thingsboard/server/service/ws/WsSessionMetaData.java
  48. 30
      application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/MarkNotificationAsReadCmd.java
  49. 28
      application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/NotificationCmdsWrapper.java
  50. 28
      application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/NotificationsSubCmd.java
  51. 24
      application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/NotificationsUnsubCmd.java
  52. 54
      application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/UnreadNotificationsUpdate.java
  53. 183
      application/src/main/java/org/thingsboard/server/service/ws/notification/sub/DefaultNotificationsSubscriptionService.java
  54. 64
      application/src/main/java/org/thingsboard/server/service/ws/notification/sub/NotificationsSubscription.java
  55. 42
      application/src/main/java/org/thingsboard/server/service/ws/notification/sub/NotificationsSubscriptionService.java
  56. 32
      application/src/main/java/org/thingsboard/server/service/ws/notification/sub/NotificationsSubscriptionUpdate.java
  57. 2
      application/src/main/java/org/thingsboard/server/service/ws/telemetry/TelemetryFeature.java
  58. 5
      application/src/main/java/org/thingsboard/server/service/ws/telemetry/TelemetryWebSocketTextMsg.java
  59. 15
      application/src/main/java/org/thingsboard/server/service/ws/telemetry/WebSocketService.java
  60. 20
      application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/TelemetryPluginCmdsWrapper.java
  61. 4
      application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v1/AttributesSubscriptionCmd.java
  62. 2
      application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v1/GetHistoryCmd.java
  63. 4
      application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v1/SubscriptionCmd.java
  64. 2
      application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v1/TelemetryPluginCmd.java
  65. 4
      application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v1/TimeseriesSubscriptionCmd.java
  66. 2
      application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/AggHistoryCmd.java
  67. 2
      application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/AggKey.java
  68. 2
      application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/AggTimeSeriesCmd.java
  69. 2
      application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/AlarmDataCmd.java
  70. 2
      application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/AlarmDataUnsubscribeCmd.java
  71. 4
      application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/AlarmDataUpdate.java
  72. 2
      application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/CmdUpdate.java
  73. 5
      application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/CmdUpdateType.java
  74. 3
      application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/DataCmd.java
  75. 4
      application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/DataUpdate.java
  76. 3
      application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/EntityCountCmd.java
  77. 2
      application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/EntityCountUnsubscribeCmd.java
  78. 8
      application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/EntityCountUpdate.java
  79. 2
      application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/EntityDataCmd.java
  80. 2
      application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/EntityDataUnsubscribeCmd.java
  81. 4
      application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/EntityDataUpdate.java
  82. 2
      application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/EntityHistoryCmd.java
  83. 2
      application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/GetTsCmd.java
  84. 2
      application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/LatestValueCmd.java
  85. 2
      application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/TimeSeriesCmd.java
  86. 2
      application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/UnsubscribeCmd.java
  87. 12
      application/src/main/java/org/thingsboard/server/service/ws/telemetry/sub/AlarmSubscriptionUpdate.java
  88. 4
      application/src/main/java/org/thingsboard/server/service/ws/telemetry/sub/SubscriptionState.java
  89. 3
      application/src/main/java/org/thingsboard/server/service/ws/telemetry/sub/TelemetrySubscriptionUpdate.java
  90. 21
      application/src/test/java/org/thingsboard/server/controller/AbstractControllerTest.java
  91. 6
      application/src/test/java/org/thingsboard/server/controller/BaseWebsocketApiTest.java
  92. 16
      application/src/test/java/org/thingsboard/server/controller/TbTestWebSocketClient.java
  93. 75
      application/src/test/java/org/thingsboard/server/service/notification/NotificationsWebSocketClient.java
  94. 147
      application/src/test/java/org/thingsboard/server/service/notification/NotificationsWsApiTest.java
  95. 8
      application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java
  96. 3
      application/src/test/java/org/thingsboard/server/transport/mqtt/attributes/AbstractMqttAttributesIntegrationTest.java
  97. 30
      common/cluster-api/src/main/proto/queue.proto
  98. 9
      common/data/src/main/java/org/thingsboard/server/common/data/StringUtils.java
  99. 17
      dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationService.java
  100. 6
      dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationTargetService.java

2
application/src/main/java/org/thingsboard/server/controller/AbstractRpcController.java

@ -43,7 +43,7 @@ import org.thingsboard.server.service.rpc.TbCoreDeviceRpcService;
import org.thingsboard.server.service.security.AccessValidator; import org.thingsboard.server.service.security.AccessValidator;
import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.model.SecurityUser;
import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.security.permission.Operation;
import org.thingsboard.server.service.telemetry.exception.ToErrorResponseEntity; import org.thingsboard.server.exception.ToErrorResponseEntity;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.util.Optional; import java.util.Optional;

20
application/src/main/java/org/thingsboard/server/controller/NotificationController.java

@ -19,6 +19,7 @@ import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
@ -29,6 +30,7 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.NotificationId; import org.thingsboard.server.common.data.id.NotificationId;
import org.thingsboard.server.common.data.id.NotificationRequestId;
import org.thingsboard.server.common.data.notification.Notification; import org.thingsboard.server.common.data.notification.Notification;
import org.thingsboard.server.common.data.notification.NotificationRequest; import org.thingsboard.server.common.data.notification.NotificationRequest;
import org.thingsboard.server.common.data.notification.NotificationStatus; import org.thingsboard.server.common.data.notification.NotificationStatus;
@ -71,7 +73,7 @@ public class NotificationController extends BaseController {
public void markNotificationAsRead(@PathVariable UUID id, public void markNotificationAsRead(@PathVariable UUID id,
@AuthenticationPrincipal SecurityUser user) { @AuthenticationPrincipal SecurityUser user) {
NotificationId notificationId = new NotificationId(id); NotificationId notificationId = new NotificationId(id);
notificationService.updateNotificationStatus(user.getTenantId(), notificationId, NotificationStatus.READ); notificationProcessingService.markNotificationAsRead(user, notificationId);
} }
@ -83,6 +85,14 @@ public class NotificationController extends BaseController {
return notificationProcessingService.processNotificationRequest(user, notificationRequest); return notificationProcessingService.processNotificationRequest(user, notificationRequest);
} }
@GetMapping("/notification/request/{id}")
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
public NotificationRequest getNotificationRequestById(@PathVariable UUID id,
@AuthenticationPrincipal SecurityUser user) {
NotificationRequestId notificationRequestId = new NotificationRequestId(id);
return notificationService.findNotificationRequestById(user.getTenantId(), notificationRequestId);
}
@GetMapping("/notification/requests") @GetMapping("/notification/requests")
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
public PageData<NotificationRequest> getNotificationRequests(@RequestParam int pageSize, public PageData<NotificationRequest> getNotificationRequests(@RequestParam int pageSize,
@ -96,9 +106,11 @@ public class NotificationController extends BaseController {
return notificationService.findNotificationRequestsByTenantIdAndPageLink(user.getTenantId(), pageLink); return notificationService.findNotificationRequestsByTenantIdAndPageLink(user.getTenantId(), pageLink);
} }
// delete request and sent notifications @DeleteMapping("/notification/request/{id}")
public void deleteNotificationRequest() { public void deleteNotificationRequest(@PathVariable UUID id,
@AuthenticationPrincipal SecurityUser user) {
NotificationRequestId notificationRequestId = new NotificationRequestId(id);
notificationProcessingService.deleteNotificationRequest(user, notificationRequestId);
} }
} }

2
application/src/main/java/org/thingsboard/server/controller/RpcV2Controller.java

@ -47,7 +47,7 @@ import org.thingsboard.server.common.msg.TbMsgMetaData;
import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.rpc.RemoveRpcActorMsg; import org.thingsboard.server.service.rpc.RemoveRpcActorMsg;
import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.security.permission.Operation;
import org.thingsboard.server.service.telemetry.exception.ToErrorResponseEntity; import org.thingsboard.server.exception.ToErrorResponseEntity;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.util.UUID; import java.util.UUID;

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

@ -84,8 +84,8 @@ import org.thingsboard.server.service.security.model.SecurityUser;
import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.security.permission.Operation;
import org.thingsboard.server.service.telemetry.AttributeData; import org.thingsboard.server.service.telemetry.AttributeData;
import org.thingsboard.server.service.telemetry.TsData; import org.thingsboard.server.service.telemetry.TsData;
import org.thingsboard.server.service.telemetry.exception.InvalidParametersException; import org.thingsboard.server.exception.InvalidParametersException;
import org.thingsboard.server.service.telemetry.exception.UncheckedApiException; import org.thingsboard.server.exception.UncheckedApiException;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;

54
application/src/main/java/org/thingsboard/server/controller/plugin/TbWebSocketHandler.java

@ -38,10 +38,11 @@ import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.model.SecurityUser;
import org.thingsboard.server.service.security.model.UserPrincipal; import org.thingsboard.server.service.security.model.UserPrincipal;
import org.thingsboard.server.service.telemetry.SessionEvent; import org.thingsboard.server.service.ws.SessionEvent;
import org.thingsboard.server.service.telemetry.TelemetryWebSocketMsgEndpoint; import org.thingsboard.server.service.ws.WebSocketMsgEndpoint;
import org.thingsboard.server.service.telemetry.TelemetryWebSocketService; import org.thingsboard.server.service.ws.WebSocketSessionType;
import org.thingsboard.server.service.telemetry.TelemetryWebSocketSessionRef; import org.thingsboard.server.service.ws.telemetry.WebSocketService;
import org.thingsboard.server.service.ws.WebSocketSessionRef;
import javax.websocket.RemoteEndpoint; import javax.websocket.RemoteEndpoint;
import javax.websocket.SendHandler; import javax.websocket.SendHandler;
@ -57,19 +58,19 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.LinkedBlockingQueue;
import static org.thingsboard.server.service.telemetry.DefaultTelemetryWebSocketService.NUMBER_OF_PING_ATTEMPTS; import static org.thingsboard.server.service.telemetry.DefaultWebSocketService.NUMBER_OF_PING_ATTEMPTS;
@Service @Service
@TbCoreComponent @TbCoreComponent
@Slf4j @Slf4j
public class TbWebSocketHandler extends TextWebSocketHandler implements TelemetryWebSocketMsgEndpoint { public class TbWebSocketHandler extends TextWebSocketHandler implements WebSocketMsgEndpoint {
private static final ConcurrentMap<String, SessionMetaData> internalSessionMap = new ConcurrentHashMap<>(); private static final ConcurrentMap<String, SessionMetaData> internalSessionMap = new ConcurrentHashMap<>();
private static final ConcurrentMap<String, String> externalSessionMap = new ConcurrentHashMap<>(); private static final ConcurrentMap<String, String> externalSessionMap = new ConcurrentHashMap<>();
@Autowired @Autowired
private TelemetryWebSocketService webSocketService; private WebSocketService webSocketService;
@Autowired @Autowired
private TbTenantProfileCache tenantProfileCache; private TbTenantProfileCache tenantProfileCache;
@ -79,7 +80,7 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements Telemetr
@Value("${server.ws.ping_timeout:30000}") @Value("${server.ws.ping_timeout:30000}")
private long pingTimeout; private long pingTimeout;
private ConcurrentMap<String, TelemetryWebSocketSessionRef> blacklistedSessions = new ConcurrentHashMap<>(); private ConcurrentMap<String, WebSocketSessionRef> blacklistedSessions = new ConcurrentHashMap<>();
private ConcurrentMap<String, TbRateLimits> perSessionUpdateLimits = new ConcurrentHashMap<>(); private ConcurrentMap<String, TbRateLimits> perSessionUpdateLimits = new ConcurrentHashMap<>();
private ConcurrentMap<TenantId, Set<String>> tenantSessionsMap = new ConcurrentHashMap<>(); private ConcurrentMap<TenantId, Set<String>> tenantSessionsMap = new ConcurrentHashMap<>();
@ -130,7 +131,7 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements Telemetr
} }
} }
String internalSessionId = session.getId(); String internalSessionId = session.getId();
TelemetryWebSocketSessionRef sessionRef = toRef(session); WebSocketSessionRef sessionRef = toRef(session);
String externalSessionId = sessionRef.getSessionId(); String externalSessionId = sessionRef.getSessionId();
if (!checkLimits(session, sessionRef)) { if (!checkLimits(session, sessionRef)) {
@ -178,7 +179,7 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements Telemetr
} }
} }
private void processInWebSocketService(TelemetryWebSocketSessionRef sessionRef, SessionEvent event) { private void processInWebSocketService(WebSocketSessionRef sessionRef, SessionEvent event) {
try { try {
webSocketService.handleWebSocketSessionEvent(sessionRef, event); webSocketService.handleWebSocketSessionEvent(sessionRef, event);
} catch (BeanCreationNotAllowedException e) { } catch (BeanCreationNotAllowedException e) {
@ -186,7 +187,7 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements Telemetr
} }
} }
private TelemetryWebSocketSessionRef toRef(WebSocketSession session) throws IOException { private WebSocketSessionRef toRef(WebSocketSession session) throws IOException {
URI sessionUri = session.getUri(); URI sessionUri = session.getUri();
String path = sessionUri.getPath(); String path = sessionUri.getPath();
path = path.substring(WebSocketConfiguration.WS_PLUGIN_PREFIX.length()); path = path.substring(WebSocketConfiguration.WS_PLUGIN_PREFIX.length());
@ -195,25 +196,30 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements Telemetr
} }
String[] pathElements = path.split("/"); String[] pathElements = path.split("/");
String serviceToken = pathElements[0]; String serviceToken = pathElements[0];
if (!"telemetry".equalsIgnoreCase(serviceToken)) { WebSocketSessionType sessionType = WebSocketSessionType.forName(serviceToken)
throw new InvalidParameterException("Can't find plugin with specified token!"); .orElseThrow(() -> new InvalidParameterException("Can't find plugin with specified token!"));
} else {
SecurityUser currentUser = (SecurityUser) ((Authentication) session.getPrincipal()).getPrincipal(); SecurityUser currentUser = (SecurityUser) ((Authentication) session.getPrincipal()).getPrincipal();
return new TelemetryWebSocketSessionRef(UUID.randomUUID().toString(), currentUser, session.getLocalAddress(), session.getRemoteAddress()); return WebSocketSessionRef.builder()
} .sessionId(UUID.randomUUID().toString())
.securityCtx(currentUser)
.localAddress(session.getLocalAddress())
.remoteAddress(session.getRemoteAddress())
.sessionType(sessionType)
.build();
} }
private class SessionMetaData implements SendHandler { private class SessionMetaData implements SendHandler {
private final WebSocketSession session; private final WebSocketSession session;
private final RemoteEndpoint.Async asyncRemote; private final RemoteEndpoint.Async asyncRemote;
private final TelemetryWebSocketSessionRef sessionRef; private final WebSocketSessionRef sessionRef;
private volatile boolean isSending = false; private volatile boolean isSending = false;
private final Queue<TbWebSocketMsg<?>> msgQueue; private final Queue<TbWebSocketMsg<?>> msgQueue;
private volatile long lastActivityTime; private volatile long lastActivityTime;
SessionMetaData(WebSocketSession session, TelemetryWebSocketSessionRef sessionRef, int maxMsgQueuePerSession) { SessionMetaData(WebSocketSession session, WebSocketSessionRef sessionRef, int maxMsgQueuePerSession) {
super(); super();
this.session = session; this.session = session;
Session nativeSession = ((NativeWebSocketSession) session).getNativeSession(Session.class); Session nativeSession = ((NativeWebSocketSession) session).getNativeSession(Session.class);
@ -309,7 +315,7 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements Telemetr
} }
@Override @Override
public void send(TelemetryWebSocketSessionRef sessionRef, int subscriptionId, String msg) throws IOException { public void send(WebSocketSessionRef sessionRef, int subscriptionId, String msg) throws IOException {
String externalId = sessionRef.getSessionId(); String externalId = sessionRef.getSessionId();
log.debug("[{}] Processing {}", externalId, msg); log.debug("[{}] Processing {}", externalId, msg);
String internalId = externalSessionMap.get(externalId); String internalId = externalSessionMap.get(externalId);
@ -343,7 +349,7 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements Telemetr
} }
@Override @Override
public void sendPing(TelemetryWebSocketSessionRef sessionRef, long currentTime) throws IOException { public void sendPing(WebSocketSessionRef sessionRef, long currentTime) throws IOException {
String externalId = sessionRef.getSessionId(); String externalId = sessionRef.getSessionId();
String internalId = externalSessionMap.get(externalId); String internalId = externalSessionMap.get(externalId);
if (internalId != null) { if (internalId != null) {
@ -359,7 +365,7 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements Telemetr
} }
@Override @Override
public void close(TelemetryWebSocketSessionRef sessionRef, CloseStatus reason) throws IOException { public void close(WebSocketSessionRef sessionRef, CloseStatus reason) throws IOException {
String externalId = sessionRef.getSessionId(); String externalId = sessionRef.getSessionId();
log.debug("[{}] Processing close request", externalId); log.debug("[{}] Processing close request", externalId);
String internalId = externalSessionMap.get(externalId); String internalId = externalSessionMap.get(externalId);
@ -375,7 +381,7 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements Telemetr
} }
} }
private boolean checkLimits(WebSocketSession session, TelemetryWebSocketSessionRef sessionRef) throws Exception { private boolean checkLimits(WebSocketSession session, WebSocketSessionRef sessionRef) throws Exception {
var tenantProfileConfiguration = var tenantProfileConfiguration =
tenantProfileCache.get(sessionRef.getSecurityCtx().getTenantId()).getDefaultProfileConfiguration(); tenantProfileCache.get(sessionRef.getSecurityCtx().getTenantId()).getDefaultProfileConfiguration();
if (tenantProfileConfiguration == null) { if (tenantProfileConfiguration == null) {
@ -443,7 +449,7 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements Telemetr
return true; return true;
} }
private void cleanupLimits(WebSocketSession session, TelemetryWebSocketSessionRef sessionRef) { private void cleanupLimits(WebSocketSession session, WebSocketSessionRef sessionRef) {
var tenantProfileConfiguration = tenantProfileCache.get(sessionRef.getSecurityCtx().getTenantId()).getDefaultProfileConfiguration(); var tenantProfileConfiguration = tenantProfileCache.get(sessionRef.getSecurityCtx().getTenantId()).getDefaultProfileConfiguration();
String sessionId = session.getId(); String sessionId = session.getId();

2
application/src/main/java/org/thingsboard/server/service/telemetry/exception/AccessDeniedException.java → application/src/main/java/org/thingsboard/server/exception/AccessDeniedException.java

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.thingsboard.server.service.telemetry.exception; package org.thingsboard.server.exception;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;

2
application/src/main/java/org/thingsboard/server/service/telemetry/exception/EntityNotFoundException.java → application/src/main/java/org/thingsboard/server/exception/EntityNotFoundException.java

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.thingsboard.server.service.telemetry.exception; package org.thingsboard.server.exception;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;

2
application/src/main/java/org/thingsboard/server/service/telemetry/exception/InternalErrorException.java → application/src/main/java/org/thingsboard/server/exception/InternalErrorException.java

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.thingsboard.server.service.telemetry.exception; package org.thingsboard.server.exception;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;

2
application/src/main/java/org/thingsboard/server/service/telemetry/exception/InvalidParametersException.java → application/src/main/java/org/thingsboard/server/exception/InvalidParametersException.java

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.thingsboard.server.service.telemetry.exception; package org.thingsboard.server.exception;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;

2
application/src/main/java/org/thingsboard/server/service/telemetry/exception/ToErrorResponseEntity.java → application/src/main/java/org/thingsboard/server/exception/ToErrorResponseEntity.java

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.thingsboard.server.service.telemetry.exception; package org.thingsboard.server.exception;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;

2
application/src/main/java/org/thingsboard/server/service/telemetry/exception/UnauthorizedException.java → application/src/main/java/org/thingsboard/server/exception/UnauthorizedException.java

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.thingsboard.server.service.telemetry.exception; package org.thingsboard.server.exception;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;

2
application/src/main/java/org/thingsboard/server/service/telemetry/exception/UncheckedApiException.java → application/src/main/java/org/thingsboard/server/exception/UncheckedApiException.java

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.thingsboard.server.service.telemetry.exception; package org.thingsboard.server.exception;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;

58
application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationProcessingService.java

@ -15,12 +15,15 @@
*/ */
package org.thingsboard.server.service.notification; package org.thingsboard.server.service.notification;
import com.google.common.base.Strings;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.thingsboard.rule.engine.api.util.TbNodeUtils; import org.thingsboard.rule.engine.api.util.TbNodeUtils;
import org.thingsboard.server.cluster.TbClusterService;
import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.NotificationId;
import org.thingsboard.server.common.data.id.NotificationRequestId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.notification.Notification; import org.thingsboard.server.common.data.notification.Notification;
@ -29,13 +32,12 @@ import org.thingsboard.server.common.data.notification.NotificationStatus;
import org.thingsboard.server.dao.notification.NotificationService; import org.thingsboard.server.dao.notification.NotificationService;
import org.thingsboard.server.dao.notification.NotificationTargetService; import org.thingsboard.server.dao.notification.NotificationTargetService;
import org.thingsboard.server.dao.user.UserService; import org.thingsboard.server.dao.user.UserService;
import org.thingsboard.server.queue.discovery.PartitionService;
import org.thingsboard.server.service.executors.DbCallbackExecutorService; import org.thingsboard.server.service.executors.DbCallbackExecutorService;
import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.model.SecurityUser;
import org.thingsboard.server.service.security.permission.AccessControlService; import org.thingsboard.server.service.security.permission.AccessControlService;
import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.security.permission.Operation;
import org.thingsboard.server.service.security.permission.Resource; import org.thingsboard.server.service.security.permission.Resource;
import org.thingsboard.server.service.telemetry.AbstractSubscriptionService; import org.thingsboard.server.service.ws.notification.sub.NotificationsSubscriptionService;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -43,26 +45,15 @@ import java.util.Map;
@Service @Service
@Slf4j @Slf4j
public class DefaultNotificationProcessingService extends AbstractSubscriptionService implements NotificationProcessingService { @RequiredArgsConstructor
public class DefaultNotificationProcessingService implements NotificationProcessingService {
private final NotificationTargetService notificationTargetService; private final NotificationTargetService notificationTargetService;
private final NotificationService notificationService; private final NotificationService notificationService;
private final UserService userService; private final UserService userService;
private final AccessControlService accessControlService; private final AccessControlService accessControlService;
private final DbCallbackExecutorService dbCallbackExecutorService; private final DbCallbackExecutorService dbCallbackExecutorService;
private final NotificationsSubscriptionService notificationsSubscriptionService;
public DefaultNotificationProcessingService(TbClusterService clusterService, PartitionService partitionService,
NotificationTargetService notificationTargetService,
NotificationService notificationService, UserService userService,
AccessControlService accessControlService,
DbCallbackExecutorService dbCallbackExecutorService) {
super(clusterService, partitionService);
this.notificationTargetService = notificationTargetService;
this.notificationService = notificationService;
this.userService = userService;
this.accessControlService = accessControlService;
this.dbCallbackExecutorService = dbCallbackExecutorService;
}
@Override @Override
public NotificationRequest processNotificationRequest(SecurityUser user, NotificationRequest notificationRequest) throws ThingsboardException { public NotificationRequest processNotificationRequest(SecurityUser user, NotificationRequest notificationRequest) throws ThingsboardException {
@ -83,19 +74,33 @@ public class DefaultNotificationProcessingService extends AbstractSubscriptionSe
for (User recipient : recipients) { for (User recipient : recipients) {
dbCallbackExecutorService.submit(() -> { dbCallbackExecutorService.submit(() -> {
Notification notification = createNotification(recipient, notificationRequest); Notification notification = createNotification(recipient, savedNotificationRequest);
onNewNotification(notification); notificationsSubscriptionService.onNewNotification(recipient.getTenantId(), recipient.getId(), notification);
}); });
} }
return savedNotificationRequest; return savedNotificationRequest;
} }
@Override
public void markNotificationAsRead(SecurityUser user, NotificationId notificationId) {
Notification notification = notificationService.updateNotificationStatus(user.getTenantId(), notificationId, NotificationStatus.READ);
notificationsSubscriptionService.onNotificationUpdated(user.getTenantId(), user.getId(), notification);
}
@Override
public void deleteNotificationRequest(SecurityUser user, NotificationRequestId notificationRequestId) {
notificationService.deleteNotificationRequest(user.getTenantId(), notificationRequestId);
notificationsSubscriptionService.onNotificationRequestDeleted(user.getTenantId(), notificationRequestId);
}
private Notification createNotification(User recipient, NotificationRequest notificationRequest) { private Notification createNotification(User recipient, NotificationRequest notificationRequest) {
Notification notification = Notification.builder() Notification notification = Notification.builder()
.requestId(notificationRequest.getId()) .requestId(notificationRequest.getId())
.recipientId(recipient.getId()) .recipientId(recipient.getId())
.reason(notificationRequest.getNotificationReason())
.text(formatNotificationText(notificationRequest.getTextTemplate(), recipient)) .text(formatNotificationText(notificationRequest.getTextTemplate(), recipient))
.info(notificationRequest.getNotificationInfo())
.severity(notificationRequest.getNotificationSeverity()) .severity(notificationRequest.getNotificationSeverity())
.status(NotificationStatus.SENT) .status(NotificationStatus.SENT)
.build(); .build();
@ -103,24 +108,15 @@ public class DefaultNotificationProcessingService extends AbstractSubscriptionSe
return notification; return notification;
} }
private void onNewNotification(Notification notification) {
wsCallBackExecutor.submit(() -> {
})
}
private String formatNotificationText(String template, User recipient) { private String formatNotificationText(String template, User recipient) {
Map<String, String> context = Map.of( Map<String, String> context = Map.of(
"recipientEmail", recipient.getEmail(), "recipientEmail", recipient.getEmail(),
"recipientFirstName", recipient.getFirstName(), "recipientFirstName", Strings.nullToEmpty(recipient.getFirstName()),
"recipientLastName", recipient.getLastName() "recipientLastName", Strings.nullToEmpty(recipient.getLastName())
); );
return TbNodeUtils.processTemplate(template, context); return TbNodeUtils.processTemplate(template, context);
} }
@Override // handle markAsRead and deleteNotificationRequest - send UnreadNotificationsUpdate with updated list
protected String getExecutorPrefix() {
return "notification";
}
} }

6
application/src/main/java/org/thingsboard/server/service/notification/NotificationProcessingService.java

@ -16,6 +16,8 @@
package org.thingsboard.server.service.notification; package org.thingsboard.server.service.notification;
import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.NotificationId;
import org.thingsboard.server.common.data.id.NotificationRequestId;
import org.thingsboard.server.common.data.notification.NotificationRequest; import org.thingsboard.server.common.data.notification.NotificationRequest;
import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.model.SecurityUser;
@ -23,4 +25,8 @@ public interface NotificationProcessingService {
NotificationRequest processNotificationRequest(SecurityUser user, NotificationRequest notificationRequest) throws ThingsboardException; NotificationRequest processNotificationRequest(SecurityUser user, NotificationRequest notificationRequest) throws ThingsboardException;
void markNotificationAsRead(SecurityUser user, NotificationId notificationId);
void deleteNotificationRequest(SecurityUser user, NotificationRequestId notificationRequestId);
} }

25
application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java

@ -27,7 +27,10 @@ import org.thingsboard.common.util.ThingsBoardThreadFactory;
import org.thingsboard.server.actors.ActorSystemContext; import org.thingsboard.server.actors.ActorSystemContext;
import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.Alarm;
import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.NotificationRequestId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.notification.Notification;
import org.thingsboard.server.common.data.rpc.RpcError; import org.thingsboard.server.common.data.rpc.RpcError;
import org.thingsboard.server.common.msg.MsgType; import org.thingsboard.server.common.msg.MsgType;
import org.thingsboard.server.common.msg.TbActorMsg; import org.thingsboard.server.common.msg.TbActorMsg;
@ -77,6 +80,7 @@ import org.thingsboard.server.service.subscription.TbLocalSubscriptionService;
import org.thingsboard.server.service.subscription.TbSubscriptionUtils; import org.thingsboard.server.service.subscription.TbSubscriptionUtils;
import org.thingsboard.server.service.sync.vc.GitVersionControlQueueService; import org.thingsboard.server.service.sync.vc.GitVersionControlQueueService;
import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper; import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper;
import org.thingsboard.server.service.ws.notification.sub.NotificationsSubscriptionUpdate;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy; import javax.annotation.PreDestroy;
@ -456,6 +460,13 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService<ToCore
localSubscriptionService.onSubscriptionUpdate(msg.getSubUpdate().getSessionId(), TbSubscriptionUtils.fromProto(msg.getSubUpdate()), callback); localSubscriptionService.onSubscriptionUpdate(msg.getSubUpdate().getSessionId(), TbSubscriptionUtils.fromProto(msg.getSubUpdate()), callback);
} else if (msg.hasAlarmSubUpdate()) { } else if (msg.hasAlarmSubUpdate()) {
localSubscriptionService.onSubscriptionUpdate(msg.getAlarmSubUpdate().getSessionId(), TbSubscriptionUtils.fromProto(msg.getAlarmSubUpdate()), callback); localSubscriptionService.onSubscriptionUpdate(msg.getAlarmSubUpdate().getSessionId(), TbSubscriptionUtils.fromProto(msg.getAlarmSubUpdate()), callback);
} else if (msg.hasNotificationsSubUpdate()) {
TransportProtos.NotificationsSubscriptionUpdateProto notificationsSubUpdateProto = msg.getNotificationsSubUpdate();
NotificationsSubscriptionUpdate notificationsSubscriptionUpdate = NotificationsSubscriptionUpdate.builder()
.notification(JacksonUtil.fromString(notificationsSubUpdateProto.getNotification(), Notification.class))
.build();
localSubscriptionService.onSubscriptionUpdate(notificationsSubUpdateProto.getSessionId(),
notificationsSubUpdateProto.getSubscriptionId(), notificationsSubscriptionUpdate, callback);
} else { } else {
throwNotHandled(msg, callback); throwNotHandled(msg, callback);
} }
@ -468,6 +479,8 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService<ToCore
subscriptionManagerService.addSubscription(TbSubscriptionUtils.fromProto(msg.getTelemetrySub()), callback); subscriptionManagerService.addSubscription(TbSubscriptionUtils.fromProto(msg.getTelemetrySub()), callback);
} else if (msg.hasAlarmSub()) { } else if (msg.hasAlarmSub()) {
subscriptionManagerService.addSubscription(TbSubscriptionUtils.fromProto(msg.getAlarmSub()), callback); subscriptionManagerService.addSubscription(TbSubscriptionUtils.fromProto(msg.getAlarmSub()), callback);
} else if (msg.hasNotificationsSub()) {
subscriptionManagerService.addSubscription(TbSubscriptionUtils.fromProto(msg.getNotificationsSub()), callback);
} else if (msg.hasSubClose()) { } else if (msg.hasSubClose()) {
TbSubscriptionCloseProto closeProto = msg.getSubClose(); TbSubscriptionCloseProto closeProto = msg.getSubClose();
subscriptionManagerService.cancelSubscription(closeProto.getSessionId(), closeProto.getSubscriptionId(), callback); subscriptionManagerService.cancelSubscription(closeProto.getSessionId(), closeProto.getSubscriptionId(), callback);
@ -507,6 +520,18 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService<ToCore
TenantId.fromUUID(new UUID(proto.getTenantIdMSB(), proto.getTenantIdLSB())), TenantId.fromUUID(new UUID(proto.getTenantIdMSB(), proto.getTenantIdLSB())),
TbSubscriptionUtils.toEntityId(proto.getEntityType(), proto.getEntityIdMSB(), proto.getEntityIdLSB()), TbSubscriptionUtils.toEntityId(proto.getEntityType(), proto.getEntityIdMSB(), proto.getEntityIdLSB()),
JacksonUtil.fromString(proto.getAlarm(), Alarm.class), callback); JacksonUtil.fromString(proto.getAlarm(), Alarm.class), callback);
} else if (msg.hasNotificationUpdate()) {
TransportProtos.NotificationUpdateProto notificationUpdateProto = msg.getNotificationUpdate();
TenantId tenantId = TenantId.fromUUID(new UUID(notificationUpdateProto.getTenantIdMSB(), notificationUpdateProto.getTenantIdLSB()));
UserId recipientId = new UserId(new UUID(notificationUpdateProto.getRecipientIdMSB(), notificationUpdateProto.getRecipientIdLSB()));
Notification notification = JacksonUtil.fromString(notificationUpdateProto.getNotification(), Notification.class);
subscriptionManagerService.onNotificationUpdate(tenantId, recipientId, notification, callback);
} else if (msg.hasNotificationRequestDelete()) {
TransportProtos.NotificationRequestDeleteProto notificationRequestDeleteProto = msg.getNotificationRequestDelete();
TenantId tenantId = TenantId.fromUUID(new UUID(notificationRequestDeleteProto.getTenantIdMSB(), notificationRequestDeleteProto.getTenantIdLSB()));
NotificationRequestId notificationRequestId = new NotificationRequestId(new UUID(
notificationRequestDeleteProto.getNotificationRequestIdMSB(), notificationRequestDeleteProto.getNotificationRequestIdLSB()));
subscriptionManagerService.onNotificationRequestDeleted(tenantId, notificationRequestId, callback);
} else { } else {
throwNotHandled(msg, callback); throwNotHandled(msg, callback);
} }

2
application/src/main/java/org/thingsboard/server/service/security/AccessValidator.java

@ -79,7 +79,7 @@ import org.thingsboard.server.service.security.model.SecurityUser;
import org.thingsboard.server.service.security.permission.AccessControlService; import org.thingsboard.server.service.security.permission.AccessControlService;
import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.security.permission.Operation;
import org.thingsboard.server.service.security.permission.Resource; import org.thingsboard.server.service.security.permission.Resource;
import org.thingsboard.server.service.telemetry.exception.ToErrorResponseEntity; import org.thingsboard.server.exception.ToErrorResponseEntity;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;

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

@ -16,10 +16,10 @@
package org.thingsboard.server.service.security; package org.thingsboard.server.service.security;
import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.FutureCallback;
import org.thingsboard.server.service.telemetry.exception.AccessDeniedException; import org.thingsboard.server.exception.AccessDeniedException;
import org.thingsboard.server.service.telemetry.exception.EntityNotFoundException; import org.thingsboard.server.exception.EntityNotFoundException;
import org.thingsboard.server.service.telemetry.exception.InternalErrorException; import org.thingsboard.server.exception.InternalErrorException;
import org.thingsboard.server.service.telemetry.exception.UnauthorizedException; import org.thingsboard.server.exception.UnauthorizedException;
/** /**
* Created by ashvayka on 31.03.18. * Created by ashvayka on 31.03.18.

1
application/src/main/java/org/thingsboard/server/service/security/permission/TenantAdminPermissions.java

@ -49,6 +49,7 @@ public class TenantAdminPermissions extends AbstractPermissions {
put(Resource.RPC, tenantEntityPermissionChecker); put(Resource.RPC, tenantEntityPermissionChecker);
put(Resource.QUEUE, queuePermissionChecker); put(Resource.QUEUE, queuePermissionChecker);
put(Resource.VERSION_CONTROL, PermissionChecker.allowAllPermissionChecker); put(Resource.VERSION_CONTROL, PermissionChecker.allowAllPermissionChecker);
put(Resource.NOTIFICATION, PermissionChecker.allowAllPermissionChecker);
} }
public static final PermissionChecker tenantEntityPermissionChecker = new PermissionChecker() { public static final PermissionChecker tenantEntityPermissionChecker = new PermissionChecker() {

49
application/src/main/java/org/thingsboard/server/service/subscription/DefaultSubscriptionManagerService.java

@ -28,7 +28,9 @@ import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.Alarm;
import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.NotificationRequestId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.kv.Aggregation; import org.thingsboard.server.common.data.kv.Aggregation;
import org.thingsboard.server.common.data.kv.AttributeKvEntry; import org.thingsboard.server.common.data.kv.AttributeKvEntry;
import org.thingsboard.server.common.data.kv.BaseReadTsKvQuery; import org.thingsboard.server.common.data.kv.BaseReadTsKvQuery;
@ -37,6 +39,7 @@ import org.thingsboard.server.common.data.kv.KvEntry;
import org.thingsboard.server.common.data.kv.ReadTsKvQuery; import org.thingsboard.server.common.data.kv.ReadTsKvQuery;
import org.thingsboard.server.common.data.kv.StringDataEntry; import org.thingsboard.server.common.data.kv.StringDataEntry;
import org.thingsboard.server.common.data.kv.TsKvEntry; import org.thingsboard.server.common.data.kv.TsKvEntry;
import org.thingsboard.server.common.data.notification.Notification;
import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.common.msg.queue.ServiceType;
import org.thingsboard.server.common.msg.queue.TbCallback; import org.thingsboard.server.common.msg.queue.TbCallback;
import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
@ -59,8 +62,9 @@ import org.thingsboard.server.queue.provider.TbQueueProducerProvider;
import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.state.DefaultDeviceStateService; import org.thingsboard.server.service.state.DefaultDeviceStateService;
import org.thingsboard.server.service.state.DeviceStateService; import org.thingsboard.server.service.state.DeviceStateService;
import org.thingsboard.server.service.telemetry.sub.AlarmSubscriptionUpdate; import org.thingsboard.server.service.ws.notification.sub.NotificationsSubscriptionUpdate;
import org.thingsboard.server.service.telemetry.sub.TelemetrySubscriptionUpdate; import org.thingsboard.server.service.ws.telemetry.sub.AlarmSubscriptionUpdate;
import org.thingsboard.server.service.ws.telemetry.sub.TelemetrySubscriptionUpdate;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy; import javax.annotation.PreDestroy;
@ -325,6 +329,47 @@ public class DefaultSubscriptionManagerService extends TbApplicationEventListene
callback.onSuccess(); callback.onSuccess();
} }
@Override
public void onNotificationUpdate(TenantId tenantId, UserId recipientId, Notification notification, TbCallback callback) {
Set<TbSubscription> subscriptions = subscriptionsByEntityId.get(recipientId);
if (subscriptions != null) {
NotificationsSubscriptionUpdate subscriptionUpdate = NotificationsSubscriptionUpdate.builder()
.notification(notification)
.build();
subscriptions.stream()
.filter(subscription -> subscription.getType() == TbSubscriptionType.NOTIFICATIONS)
.forEach(subscription -> {
if (serviceId.equals(subscription.getServiceId())) {
localSubscriptionService.onSubscriptionUpdate(subscription.getSessionId(),
subscription.getSubscriptionId(), subscriptionUpdate, TbCallback.EMPTY);
} else {
TopicPartitionInfo tpi = notificationsTopicService.getNotificationsTopic(ServiceType.TB_CORE, subscription.getServiceId());
ToCoreNotificationMsg updateProto = TbSubscriptionUtils.notificationsSubUpdateToProto(subscription, subscriptionUpdate);
TbProtoQueueMsg<ToCoreNotificationMsg> queueMsg = new TbProtoQueueMsg<>(subscription.getEntityId().getId(), updateProto);
toCoreNotificationsProducer.send(tpi, queueMsg, null);
}
});
}
callback.onSuccess();
}
@Override
public void onNotificationRequestDeleted(TenantId tenantId, NotificationRequestId notificationRequestId, TbCallback callback) {
NotificationsSubscriptionUpdate subscriptionUpdate = NotificationsSubscriptionUpdate.builder()
.notificationRequestDeleted(true)
.notificationRequestId(notificationRequestId)
.build();
subscriptionsByEntityId.entrySet().stream()
.filter(subEntry -> subEntry.getKey().getEntityType() == EntityType.USER)
.flatMap(subEntry -> subEntry.getValue().stream()
.filter(sub -> sub.getType() == TbSubscriptionType.NOTIFICATIONS)
.filter(sub -> sub.getServiceId().equals(serviceId)))
.forEach(subscription -> {
localSubscriptionService.onSubscriptionUpdate(subscription.getSessionId(), subscription.getSubscriptionId(), subscriptionUpdate, TbCallback.EMPTY);
});
callback.onSuccess();
}
@Override @Override
public void onAttributesDelete(TenantId tenantId, EntityId entityId, String scope, List<String> keys, TbCallback callback) { public void onAttributesDelete(TenantId tenantId, EntityId entityId, String scope, List<String> keys, TbCallback callback) {
onLocalTelemetrySubUpdate(entityId, onLocalTelemetrySubUpdate(entityId,

45
application/src/main/java/org/thingsboard/server/service/subscription/DefaultTbEntityDataSubscriptionService.java

@ -48,22 +48,21 @@ import org.thingsboard.server.dao.timeseries.TimeseriesService;
import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; import org.thingsboard.server.queue.discovery.TbServiceInfoProvider;
import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.executors.DbCallbackExecutorService; import org.thingsboard.server.service.executors.DbCallbackExecutorService;
import org.thingsboard.server.service.telemetry.TelemetryWebSocketService; import org.thingsboard.server.service.ws.telemetry.WebSocketService;
import org.thingsboard.server.service.telemetry.TelemetryWebSocketSessionRef; import org.thingsboard.server.service.ws.WebSocketSessionRef;
import org.thingsboard.server.service.telemetry.cmd.v2.AggHistoryCmd; import org.thingsboard.server.service.ws.telemetry.cmd.v2.AggHistoryCmd;
import org.thingsboard.server.service.telemetry.cmd.v2.AggKey; import org.thingsboard.server.service.ws.telemetry.cmd.v2.AggKey;
import org.thingsboard.server.service.telemetry.cmd.v2.AggTimeSeriesCmd; import org.thingsboard.server.service.ws.telemetry.cmd.v2.AggTimeSeriesCmd;
import org.thingsboard.server.service.telemetry.cmd.v2.AlarmDataCmd; import org.thingsboard.server.service.ws.telemetry.cmd.v2.AlarmDataCmd;
import org.thingsboard.server.service.telemetry.cmd.v2.AlarmDataUpdate; import org.thingsboard.server.service.ws.telemetry.cmd.v2.AlarmDataUpdate;
import org.thingsboard.server.service.telemetry.cmd.v2.EntityCountCmd; import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityCountCmd;
import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataCmd; import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityDataCmd;
import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataUpdate; import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityDataUpdate;
import org.thingsboard.server.service.telemetry.cmd.v2.EntityHistoryCmd; import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityHistoryCmd;
import org.thingsboard.server.service.telemetry.cmd.v2.GetTsCmd; import org.thingsboard.server.service.ws.telemetry.cmd.v2.GetTsCmd;
import org.thingsboard.server.service.telemetry.cmd.v2.LatestValueCmd; import org.thingsboard.server.service.ws.telemetry.cmd.v2.LatestValueCmd;
import org.thingsboard.server.service.telemetry.cmd.v2.TimeSeriesCmd; import org.thingsboard.server.service.ws.telemetry.cmd.v2.TimeSeriesCmd;
import org.thingsboard.server.service.telemetry.cmd.v2.UnsubscribeCmd; import org.thingsboard.server.service.ws.telemetry.cmd.v2.UnsubscribeCmd;
import org.thingsboard.server.service.telemetry.sub.SubscriptionErrorCode;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy; import javax.annotation.PreDestroy;
@ -95,7 +94,7 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc
private final Map<String, Map<Integer, TbAbstractSubCtx>> subscriptionsBySessionId = new ConcurrentHashMap<>(); private final Map<String, Map<Integer, TbAbstractSubCtx>> subscriptionsBySessionId = new ConcurrentHashMap<>();
@Autowired @Autowired
private TelemetryWebSocketService wsService; private WebSocketService wsService;
@Autowired @Autowired
private EntityService entityService; private EntityService entityService;
@ -166,7 +165,7 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc
} }
@Override @Override
public void handleCmd(TelemetryWebSocketSessionRef session, EntityDataCmd cmd) { public void handleCmd(WebSocketSessionRef session, EntityDataCmd cmd) {
TbEntityDataSubCtx ctx = getSubCtx(session.getSessionId(), cmd.getCmdId()); TbEntityDataSubCtx ctx = getSubCtx(session.getSessionId(), cmd.getCmdId());
if (ctx != null) { if (ctx != null) {
log.debug("[{}][{}] Updating existing subscriptions using: {}", session.getSessionId(), cmd.getCmdId(), cmd); log.debug("[{}][{}] Updating existing subscriptions using: {}", session.getSessionId(), cmd.getCmdId(), cmd);
@ -357,7 +356,7 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc
} }
@Override @Override
public void handleCmd(TelemetryWebSocketSessionRef session, EntityCountCmd cmd) { public void handleCmd(WebSocketSessionRef session, EntityCountCmd cmd) {
TbEntityCountSubCtx ctx = getSubCtx(session.getSessionId(), cmd.getCmdId()); TbEntityCountSubCtx ctx = getSubCtx(session.getSessionId(), cmd.getCmdId());
if (ctx == null) { if (ctx == null) {
ctx = createSubCtx(session, cmd); ctx = createSubCtx(session, cmd);
@ -377,7 +376,7 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc
} }
@Override @Override
public void handleCmd(TelemetryWebSocketSessionRef session, AlarmDataCmd cmd) { public void handleCmd(WebSocketSessionRef session, AlarmDataCmd cmd) {
TbAlarmDataSubCtx ctx = getSubCtx(session.getSessionId(), cmd.getCmdId()); TbAlarmDataSubCtx ctx = getSubCtx(session.getSessionId(), cmd.getCmdId());
if (ctx == null) { if (ctx == null) {
log.debug("[{}][{}] Creating new alarm subscription using: {}", session.getSessionId(), cmd.getCmdId(), cmd); log.debug("[{}][{}] Creating new alarm subscription using: {}", session.getSessionId(), cmd.getCmdId(), cmd);
@ -468,7 +467,7 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc
} }
} }
private TbEntityDataSubCtx createSubCtx(TelemetryWebSocketSessionRef sessionRef, EntityDataCmd cmd) { private TbEntityDataSubCtx createSubCtx(WebSocketSessionRef sessionRef, EntityDataCmd cmd) {
Map<Integer, TbAbstractSubCtx> sessionSubs = subscriptionsBySessionId.computeIfAbsent(sessionRef.getSessionId(), k -> new HashMap<>()); Map<Integer, TbAbstractSubCtx> sessionSubs = subscriptionsBySessionId.computeIfAbsent(sessionRef.getSessionId(), k -> new HashMap<>());
TbEntityDataSubCtx ctx = new TbEntityDataSubCtx(serviceId, wsService, entityService, localSubscriptionService, TbEntityDataSubCtx ctx = new TbEntityDataSubCtx(serviceId, wsService, entityService, localSubscriptionService,
attributesService, stats, sessionRef, cmd.getCmdId(), maxEntitiesPerDataSubscription); attributesService, stats, sessionRef, cmd.getCmdId(), maxEntitiesPerDataSubscription);
@ -479,7 +478,7 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc
return ctx; return ctx;
} }
private TbEntityCountSubCtx createSubCtx(TelemetryWebSocketSessionRef sessionRef, EntityCountCmd cmd) { private TbEntityCountSubCtx createSubCtx(WebSocketSessionRef sessionRef, EntityCountCmd cmd) {
Map<Integer, TbAbstractSubCtx> sessionSubs = subscriptionsBySessionId.computeIfAbsent(sessionRef.getSessionId(), k -> new HashMap<>()); Map<Integer, TbAbstractSubCtx> sessionSubs = subscriptionsBySessionId.computeIfAbsent(sessionRef.getSessionId(), k -> new HashMap<>());
TbEntityCountSubCtx ctx = new TbEntityCountSubCtx(serviceId, wsService, entityService, localSubscriptionService, TbEntityCountSubCtx ctx = new TbEntityCountSubCtx(serviceId, wsService, entityService, localSubscriptionService,
attributesService, stats, sessionRef, cmd.getCmdId()); attributesService, stats, sessionRef, cmd.getCmdId());
@ -491,7 +490,7 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc
} }
private TbAlarmDataSubCtx createSubCtx(TelemetryWebSocketSessionRef sessionRef, AlarmDataCmd cmd) { private TbAlarmDataSubCtx createSubCtx(WebSocketSessionRef sessionRef, AlarmDataCmd cmd) {
Map<Integer, TbAbstractSubCtx> sessionSubs = subscriptionsBySessionId.computeIfAbsent(sessionRef.getSessionId(), k -> new HashMap<>()); Map<Integer, TbAbstractSubCtx> sessionSubs = subscriptionsBySessionId.computeIfAbsent(sessionRef.getSessionId(), k -> new HashMap<>());
TbAlarmDataSubCtx ctx = new TbAlarmDataSubCtx(serviceId, wsService, entityService, localSubscriptionService, TbAlarmDataSubCtx ctx = new TbAlarmDataSubCtx(serviceId, wsService, entityService, localSubscriptionService,
attributesService, stats, alarmService, sessionRef, cmd.getCmdId(), maxEntitiesPerAlarmSubscription, attributesService, stats, alarmService, sessionRef, cmd.getCmdId(), maxEntitiesPerAlarmSubscription,

30
application/src/main/java/org/thingsboard/server/service/subscription/DefaultTbLocalSubscriptionService.java

@ -21,18 +21,19 @@ import org.springframework.context.annotation.Lazy;
import org.springframework.context.event.EventListener; import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.thingsboard.common.util.ThingsBoardExecutors; import org.thingsboard.common.util.ThingsBoardExecutors;
import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.cluster.TbClusterService;
import org.thingsboard.server.queue.discovery.event.ClusterTopologyChangeEvent;
import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
import org.thingsboard.server.queue.discovery.PartitionService;
import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.common.msg.queue.ServiceType;
import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
import org.thingsboard.server.common.msg.queue.TbCallback; import org.thingsboard.server.common.msg.queue.TbCallback;
import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.queue.discovery.PartitionService;
import org.thingsboard.server.queue.discovery.TbApplicationEventListener; import org.thingsboard.server.queue.discovery.TbApplicationEventListener;
import org.thingsboard.server.queue.discovery.event.ClusterTopologyChangeEvent;
import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.cluster.TbClusterService; import org.thingsboard.server.service.ws.notification.sub.NotificationsSubscriptionUpdate;
import org.thingsboard.server.service.telemetry.sub.AlarmSubscriptionUpdate; import org.thingsboard.server.service.ws.telemetry.sub.AlarmSubscriptionUpdate;
import org.thingsboard.server.service.telemetry.sub.TelemetrySubscriptionUpdate; import org.thingsboard.server.service.ws.telemetry.sub.TelemetrySubscriptionUpdate;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy; import javax.annotation.PreDestroy;
@ -152,7 +153,7 @@ public class DefaultTbLocalSubscriptionService implements TbLocalSubscriptionSer
update.getLatestValues().forEach((key, value) -> attrSub.getKeyStates().put(key, value)); update.getLatestValues().forEach((key, value) -> attrSub.getKeyStates().put(key, value));
break; break;
} }
subscriptionUpdateExecutor.submit(() -> subscription.getUpdateConsumer().accept(sessionId, update)); subscriptionUpdateExecutor.submit(() -> subscription.getUpdateProcessor().accept(subscription, update));
} }
callback.onSuccess(); callback.onSuccess();
} }
@ -163,7 +164,16 @@ public class DefaultTbLocalSubscriptionService implements TbLocalSubscriptionSer
TbSubscription subscription = subscriptionsBySessionId TbSubscription subscription = subscriptionsBySessionId
.getOrDefault(sessionId, Collections.emptyMap()).get(update.getSubscriptionId()); .getOrDefault(sessionId, Collections.emptyMap()).get(update.getSubscriptionId());
if (subscription != null && subscription.getType() == TbSubscriptionType.ALARMS) { if (subscription != null && subscription.getType() == TbSubscriptionType.ALARMS) {
subscriptionUpdateExecutor.submit(() -> subscription.getUpdateConsumer().accept(sessionId, update)); subscriptionUpdateExecutor.submit(() -> subscription.getUpdateProcessor().accept(subscription, update));
}
callback.onSuccess();
}
@Override
public void onSubscriptionUpdate(String sessionId, int subscriptionId, NotificationsSubscriptionUpdate update, TbCallback callback) {
TbSubscription subscription = subscriptionsBySessionId.getOrDefault(sessionId, Collections.emptyMap()).get(subscriptionId);
if (subscription != null && subscription.getType() == TbSubscriptionType.NOTIFICATIONS) {
subscriptionUpdateExecutor.submit(() -> subscription.getUpdateProcessor().accept(subscription, update));
} }
callback.onSuccess(); callback.onSuccess();
} }

2
application/src/main/java/org/thingsboard/server/service/subscription/ReadTsKvQueryInfo.java

@ -17,7 +17,7 @@ package org.thingsboard.server.service.subscription;
import lombok.Data; import lombok.Data;
import org.thingsboard.server.common.data.kv.ReadTsKvQuery; import org.thingsboard.server.common.data.kv.ReadTsKvQuery;
import org.thingsboard.server.service.telemetry.cmd.v2.AggKey; import org.thingsboard.server.service.ws.telemetry.cmd.v2.AggKey;
@Data @Data
public class ReadTsKvQueryInfo { public class ReadTsKvQueryInfo {

2
application/src/main/java/org/thingsboard/server/service/telemetry/sub/SubscriptionErrorCode.java → application/src/main/java/org/thingsboard/server/service/subscription/SubscriptionErrorCode.java

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.thingsboard.server.service.telemetry.sub; package org.thingsboard.server.service.subscription;
public enum SubscriptionErrorCode { public enum SubscriptionErrorCode {

6
application/src/main/java/org/thingsboard/server/service/subscription/SubscriptionManagerService.java

@ -18,9 +18,12 @@ package org.thingsboard.server.service.subscription;
import org.springframework.context.ApplicationListener; import org.springframework.context.ApplicationListener;
import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.Alarm;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.NotificationRequestId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.kv.AttributeKvEntry; import org.thingsboard.server.common.data.kv.AttributeKvEntry;
import org.thingsboard.server.common.data.kv.TsKvEntry; import org.thingsboard.server.common.data.kv.TsKvEntry;
import org.thingsboard.server.common.data.notification.Notification;
import org.thingsboard.server.common.msg.queue.TbCallback; import org.thingsboard.server.common.msg.queue.TbCallback;
import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent; import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
@ -46,5 +49,8 @@ public interface SubscriptionManagerService extends ApplicationListener<Partitio
void onAlarmDeleted(TenantId tenantId, EntityId entityId, Alarm alarm, TbCallback callback); void onAlarmDeleted(TenantId tenantId, EntityId entityId, Alarm alarm, TbCallback callback);
void onNotificationUpdate(TenantId tenantId, UserId recipientId, Notification notification, TbCallback callback);
void onNotificationRequestDeleted(TenantId tenantId, NotificationRequestId notificationRequestId, TbCallback callback);
} }

14
application/src/main/java/org/thingsboard/server/service/subscription/TbAbstractDataSubCtx.java

@ -28,9 +28,9 @@ import org.thingsboard.server.common.data.query.EntityKeyType;
import org.thingsboard.server.common.data.query.TsValue; import org.thingsboard.server.common.data.query.TsValue;
import org.thingsboard.server.dao.attributes.AttributesService; import org.thingsboard.server.dao.attributes.AttributesService;
import org.thingsboard.server.dao.entity.EntityService; import org.thingsboard.server.dao.entity.EntityService;
import org.thingsboard.server.service.telemetry.TelemetryWebSocketService; import org.thingsboard.server.service.ws.WebSocketSessionRef;
import org.thingsboard.server.service.telemetry.TelemetryWebSocketSessionRef; import org.thingsboard.server.service.ws.telemetry.WebSocketService;
import org.thingsboard.server.service.telemetry.sub.TelemetrySubscriptionUpdate; import org.thingsboard.server.service.ws.telemetry.sub.TelemetrySubscriptionUpdate;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
@ -49,10 +49,10 @@ public abstract class TbAbstractDataSubCtx<T extends AbstractDataQuery<? extends
@Getter @Getter
protected PageData<EntityData> data; protected PageData<EntityData> data;
public TbAbstractDataSubCtx(String serviceId, TelemetryWebSocketService wsService, public TbAbstractDataSubCtx(String serviceId, WebSocketService wsService,
EntityService entityService, TbLocalSubscriptionService localSubscriptionService, EntityService entityService, TbLocalSubscriptionService localSubscriptionService,
AttributesService attributesService, SubscriptionServiceStatistics stats, AttributesService attributesService, SubscriptionServiceStatistics stats,
TelemetryWebSocketSessionRef sessionRef, int cmdId) { WebSocketSessionRef sessionRef, int cmdId) {
super(serviceId, wsService, entityService, localSubscriptionService, attributesService, stats, sessionRef, cmdId); super(serviceId, wsService, entityService, localSubscriptionService, attributesService, stats, sessionRef, cmdId);
this.subToEntityIdMap = new ConcurrentHashMap<>(); this.subToEntityIdMap = new ConcurrentHashMap<>();
} }
@ -184,7 +184,7 @@ public abstract class TbAbstractDataSubCtx<T extends AbstractDataQuery<? extends
.subscriptionId(subIdx) .subscriptionId(subIdx)
.tenantId(sessionRef.getSecurityCtx().getTenantId()) .tenantId(sessionRef.getSecurityCtx().getTenantId())
.entityId(entityData.getEntityId()) .entityId(entityData.getEntityId())
.updateConsumer((s, subscriptionUpdate) -> sendWsMsg(s, subscriptionUpdate, keysType)) .updateProcessor((sub, subscriptionUpdate) -> sendWsMsg(sub.getSessionId(), subscriptionUpdate, keysType))
.allKeys(false) .allKeys(false)
.keyStates(keyStates) .keyStates(keyStates)
.scope(scope) .scope(scope)
@ -215,7 +215,7 @@ public abstract class TbAbstractDataSubCtx<T extends AbstractDataQuery<? extends
.subscriptionId(subIdx) .subscriptionId(subIdx)
.tenantId(sessionRef.getSecurityCtx().getTenantId()) .tenantId(sessionRef.getSecurityCtx().getTenantId())
.entityId(entityData.getEntityId()) .entityId(entityData.getEntityId())
.updateConsumer((sessionId, subscriptionUpdate) -> sendWsMsg(sessionId, subscriptionUpdate, EntityKeyType.TIME_SERIES, resultToLatestValues)) .updateProcessor((sub, subscriptionUpdate) -> sendWsMsg(sub.getSessionId(), subscriptionUpdate, EntityKeyType.TIME_SERIES, resultToLatestValues))
.allKeys(false) .allKeys(false)
.keyStates(keyStates) .keyStates(keyStates)
.latestValues(latestValues) .latestValues(latestValues)

19
application/src/main/java/org/thingsboard/server/service/subscription/TbAbstractSubCtx.java

@ -31,7 +31,6 @@ import org.thingsboard.server.common.data.query.ComplexFilterPredicate;
import org.thingsboard.server.common.data.query.DynamicValue; import org.thingsboard.server.common.data.query.DynamicValue;
import org.thingsboard.server.common.data.query.DynamicValueSourceType; import org.thingsboard.server.common.data.query.DynamicValueSourceType;
import org.thingsboard.server.common.data.query.EntityCountQuery; import org.thingsboard.server.common.data.query.EntityCountQuery;
import org.thingsboard.server.common.data.query.EntityKeyType;
import org.thingsboard.server.common.data.query.FilterPredicateType; import org.thingsboard.server.common.data.query.FilterPredicateType;
import org.thingsboard.server.common.data.query.KeyFilter; import org.thingsboard.server.common.data.query.KeyFilter;
import org.thingsboard.server.common.data.query.KeyFilterPredicate; import org.thingsboard.server.common.data.query.KeyFilterPredicate;
@ -39,10 +38,10 @@ import org.thingsboard.server.common.data.query.SimpleKeyFilterPredicate;
import org.thingsboard.server.common.data.query.TsValue; import org.thingsboard.server.common.data.query.TsValue;
import org.thingsboard.server.dao.attributes.AttributesService; import org.thingsboard.server.dao.attributes.AttributesService;
import org.thingsboard.server.dao.entity.EntityService; import org.thingsboard.server.dao.entity.EntityService;
import org.thingsboard.server.service.telemetry.TelemetryWebSocketService; import org.thingsboard.server.service.ws.WebSocketSessionRef;
import org.thingsboard.server.service.telemetry.TelemetryWebSocketSessionRef; import org.thingsboard.server.service.ws.telemetry.WebSocketService;
import org.thingsboard.server.service.telemetry.cmd.v2.CmdUpdate; import org.thingsboard.server.service.ws.telemetry.cmd.v2.CmdUpdate;
import org.thingsboard.server.service.telemetry.sub.TelemetrySubscriptionUpdate; import org.thingsboard.server.service.ws.telemetry.sub.TelemetrySubscriptionUpdate;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
@ -64,11 +63,11 @@ public abstract class TbAbstractSubCtx<T extends EntityCountQuery> {
protected final Lock wsLock = new ReentrantLock(true); protected final Lock wsLock = new ReentrantLock(true);
protected final String serviceId; protected final String serviceId;
protected final SubscriptionServiceStatistics stats; protected final SubscriptionServiceStatistics stats;
private final TelemetryWebSocketService wsService; private final WebSocketService wsService;
protected final EntityService entityService; protected final EntityService entityService;
protected final TbLocalSubscriptionService localSubscriptionService; protected final TbLocalSubscriptionService localSubscriptionService;
protected final AttributesService attributesService; protected final AttributesService attributesService;
protected final TelemetryWebSocketSessionRef sessionRef; protected final WebSocketSessionRef sessionRef;
protected final int cmdId; protected final int cmdId;
protected final Set<Integer> subToDynamicValueKeySet; protected final Set<Integer> subToDynamicValueKeySet;
@Getter @Getter
@ -80,10 +79,10 @@ public abstract class TbAbstractSubCtx<T extends EntityCountQuery> {
protected volatile ScheduledFuture<?> refreshTask; protected volatile ScheduledFuture<?> refreshTask;
protected volatile boolean stopped; protected volatile boolean stopped;
public TbAbstractSubCtx(String serviceId, TelemetryWebSocketService wsService, public TbAbstractSubCtx(String serviceId, WebSocketService wsService,
EntityService entityService, TbLocalSubscriptionService localSubscriptionService, EntityService entityService, TbLocalSubscriptionService localSubscriptionService,
AttributesService attributesService, SubscriptionServiceStatistics stats, AttributesService attributesService, SubscriptionServiceStatistics stats,
TelemetryWebSocketSessionRef sessionRef, int cmdId) { WebSocketSessionRef sessionRef, int cmdId) {
this.serviceId = serviceId; this.serviceId = serviceId;
this.wsService = wsService; this.wsService = wsService;
this.entityService = entityService; this.entityService = entityService;
@ -142,7 +141,7 @@ public abstract class TbAbstractSubCtx<T extends EntityCountQuery> {
.subscriptionId(subIdx) .subscriptionId(subIdx)
.tenantId(sessionRef.getSecurityCtx().getTenantId()) .tenantId(sessionRef.getSecurityCtx().getTenantId())
.entityId(entityId) .entityId(entityId)
.updateConsumer((s, subscriptionUpdate) -> dynamicValueSubUpdate(s, subscriptionUpdate, dynamicValueKeySubMap)) .updateProcessor((subscription, subscriptionUpdate) -> dynamicValueSubUpdate(subscription.getSessionId(), subscriptionUpdate, dynamicValueKeySubMap))
.allKeys(false) .allKeys(false)
.keyStates(keyStates) .keyStates(keyStates)
.scope(TbAttributeSubscriptionScope.SERVER_SCOPE) .scope(TbAttributeSubscriptionScope.SERVER_SCOPE)

16
application/src/main/java/org/thingsboard/server/service/subscription/TbAlarmDataSubCtx.java

@ -38,11 +38,11 @@ import org.thingsboard.server.dao.alarm.AlarmService;
import org.thingsboard.server.dao.attributes.AttributesService; import org.thingsboard.server.dao.attributes.AttributesService;
import org.thingsboard.server.dao.entity.EntityService; import org.thingsboard.server.dao.entity.EntityService;
import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.service.telemetry.TelemetryWebSocketService; import org.thingsboard.server.service.ws.telemetry.WebSocketService;
import org.thingsboard.server.service.telemetry.TelemetryWebSocketSessionRef; import org.thingsboard.server.service.ws.WebSocketSessionRef;
import org.thingsboard.server.service.telemetry.cmd.v2.AlarmDataUpdate; import org.thingsboard.server.service.ws.telemetry.cmd.v2.AlarmDataUpdate;
import org.thingsboard.server.service.telemetry.sub.AlarmSubscriptionUpdate; import org.thingsboard.server.service.ws.telemetry.sub.AlarmSubscriptionUpdate;
import org.thingsboard.server.service.telemetry.sub.TelemetrySubscriptionUpdate; import org.thingsboard.server.service.ws.telemetry.sub.TelemetrySubscriptionUpdate;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
@ -81,10 +81,10 @@ public class TbAlarmDataSubCtx extends TbAbstractDataSubCtx<AlarmDataQuery> {
private int alarmInvocationAttempts; private int alarmInvocationAttempts;
public TbAlarmDataSubCtx(String serviceId, TelemetryWebSocketService wsService, public TbAlarmDataSubCtx(String serviceId, WebSocketService wsService,
EntityService entityService, TbLocalSubscriptionService localSubscriptionService, EntityService entityService, TbLocalSubscriptionService localSubscriptionService,
AttributesService attributesService, SubscriptionServiceStatistics stats, AlarmService alarmService, AttributesService attributesService, SubscriptionServiceStatistics stats, AlarmService alarmService,
TelemetryWebSocketSessionRef sessionRef, int cmdId, WebSocketSessionRef sessionRef, int cmdId,
int maxEntitiesPerAlarmSubscription, int maxAlarmQueriesPerRefreshInterval) { int maxEntitiesPerAlarmSubscription, int maxAlarmQueriesPerRefreshInterval) {
super(serviceId, wsService, entityService, localSubscriptionService, attributesService, stats, sessionRef, cmdId); super(serviceId, wsService, entityService, localSubscriptionService, attributesService, stats, sessionRef, cmdId);
this.maxEntitiesPerAlarmSubscription = maxEntitiesPerAlarmSubscription; this.maxEntitiesPerAlarmSubscription = maxEntitiesPerAlarmSubscription;
@ -175,7 +175,7 @@ public class TbAlarmDataSubCtx extends TbAbstractDataSubCtx<AlarmDataQuery> {
.subscriptionId(subIdx) .subscriptionId(subIdx)
.tenantId(sessionRef.getSecurityCtx().getTenantId()) .tenantId(sessionRef.getSecurityCtx().getTenantId())
.entityId(entityData.getEntityId()) .entityId(entityData.getEntityId())
.updateConsumer(this::sendWsMsg) .updateProcessor((sub, update) -> sendWsMsg(sub.getSessionId(), update))
.ts(startTs) .ts(startTs)
.build(); .build();
localSubscriptionService.addSubscription(subscription); localSubscriptionService.addSubscription(subscription);

9
application/src/main/java/org/thingsboard/server/service/subscription/TbAlarmsSubscription.java

@ -17,13 +17,10 @@ package org.thingsboard.server.service.subscription;
import lombok.Builder; import lombok.Builder;
import lombok.Getter; import lombok.Getter;
import org.thingsboard.server.common.data.alarm.AlarmSearchStatus;
import org.thingsboard.server.common.data.alarm.AlarmSeverity;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.service.telemetry.sub.AlarmSubscriptionUpdate; import org.thingsboard.server.service.ws.telemetry.sub.AlarmSubscriptionUpdate;
import java.util.List;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
public class TbAlarmsSubscription extends TbSubscription<AlarmSubscriptionUpdate> { public class TbAlarmsSubscription extends TbSubscription<AlarmSubscriptionUpdate> {
@ -33,8 +30,8 @@ public class TbAlarmsSubscription extends TbSubscription<AlarmSubscriptionUpdate
@Builder @Builder
public TbAlarmsSubscription(String serviceId, String sessionId, int subscriptionId, TenantId tenantId, EntityId entityId, public TbAlarmsSubscription(String serviceId, String sessionId, int subscriptionId, TenantId tenantId, EntityId entityId,
BiConsumer<String, AlarmSubscriptionUpdate> updateConsumer, long ts) { BiConsumer<TbSubscription<AlarmSubscriptionUpdate>, AlarmSubscriptionUpdate> updateProcessor, long ts) {
super(serviceId, sessionId, subscriptionId, tenantId, entityId, TbSubscriptionType.ALARMS, updateConsumer); super(serviceId, sessionId, subscriptionId, tenantId, entityId, TbSubscriptionType.ALARMS, updateProcessor);
this.ts = ts; this.ts = ts;
} }

6
application/src/main/java/org/thingsboard/server/service/subscription/TbAttributeSubscription.java

@ -19,7 +19,7 @@ import lombok.Builder;
import lombok.Getter; import lombok.Getter;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.service.telemetry.sub.TelemetrySubscriptionUpdate; import org.thingsboard.server.service.ws.telemetry.sub.TelemetrySubscriptionUpdate;
import java.util.Map; import java.util.Map;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
@ -32,9 +32,9 @@ public class TbAttributeSubscription extends TbSubscription<TelemetrySubscriptio
@Builder @Builder
public TbAttributeSubscription(String serviceId, String sessionId, int subscriptionId, TenantId tenantId, EntityId entityId, public TbAttributeSubscription(String serviceId, String sessionId, int subscriptionId, TenantId tenantId, EntityId entityId,
BiConsumer<String, TelemetrySubscriptionUpdate> updateConsumer, BiConsumer<TbSubscription<TelemetrySubscriptionUpdate>, TelemetrySubscriptionUpdate> updateProcessor,
boolean allKeys, Map<String, Long> keyStates, TbAttributeSubscriptionScope scope) { boolean allKeys, Map<String, Long> keyStates, TbAttributeSubscriptionScope scope) {
super(serviceId, sessionId, subscriptionId, tenantId, entityId, TbSubscriptionType.ATTRIBUTES, updateConsumer); super(serviceId, sessionId, subscriptionId, tenantId, entityId, TbSubscriptionType.ATTRIBUTES, updateProcessor);
this.allKeys = allKeys; this.allKeys = allKeys;
this.keyStates = keyStates; this.keyStates = keyStates;
this.scope = scope; this.scope = scope;

10
application/src/main/java/org/thingsboard/server/service/subscription/TbEntityCountSubCtx.java

@ -19,18 +19,18 @@ import lombok.extern.slf4j.Slf4j;
import org.thingsboard.server.common.data.query.EntityCountQuery; import org.thingsboard.server.common.data.query.EntityCountQuery;
import org.thingsboard.server.dao.attributes.AttributesService; import org.thingsboard.server.dao.attributes.AttributesService;
import org.thingsboard.server.dao.entity.EntityService; import org.thingsboard.server.dao.entity.EntityService;
import org.thingsboard.server.service.telemetry.TelemetryWebSocketService; import org.thingsboard.server.service.ws.telemetry.WebSocketService;
import org.thingsboard.server.service.telemetry.TelemetryWebSocketSessionRef; import org.thingsboard.server.service.ws.WebSocketSessionRef;
import org.thingsboard.server.service.telemetry.cmd.v2.EntityCountUpdate; import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityCountUpdate;
@Slf4j @Slf4j
public class TbEntityCountSubCtx extends TbAbstractSubCtx<EntityCountQuery> { public class TbEntityCountSubCtx extends TbAbstractSubCtx<EntityCountQuery> {
private volatile int result; private volatile int result;
public TbEntityCountSubCtx(String serviceId, TelemetryWebSocketService wsService, EntityService entityService, public TbEntityCountSubCtx(String serviceId, WebSocketService wsService, EntityService entityService,
TbLocalSubscriptionService localSubscriptionService, AttributesService attributesService, TbLocalSubscriptionService localSubscriptionService, AttributesService attributesService,
SubscriptionServiceStatistics stats, TelemetryWebSocketSessionRef sessionRef, int cmdId) { SubscriptionServiceStatistics stats, WebSocketSessionRef sessionRef, int cmdId) {
super(serviceId, wsService, entityService, localSubscriptionService, attributesService, stats, sessionRef, cmdId); super(serviceId, wsService, entityService, localSubscriptionService, attributesService, stats, sessionRef, cmdId);
} }

18
application/src/main/java/org/thingsboard/server/service/subscription/TbEntityDataSubCtx.java

@ -27,13 +27,13 @@ import org.thingsboard.server.common.data.query.EntityKeyType;
import org.thingsboard.server.common.data.query.TsValue; import org.thingsboard.server.common.data.query.TsValue;
import org.thingsboard.server.dao.attributes.AttributesService; import org.thingsboard.server.dao.attributes.AttributesService;
import org.thingsboard.server.dao.entity.EntityService; import org.thingsboard.server.dao.entity.EntityService;
import org.thingsboard.server.service.telemetry.TelemetryWebSocketService; import org.thingsboard.server.service.ws.telemetry.WebSocketService;
import org.thingsboard.server.service.telemetry.TelemetryWebSocketSessionRef; import org.thingsboard.server.service.ws.WebSocketSessionRef;
import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataCmd; import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityDataCmd;
import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataUpdate; import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityDataUpdate;
import org.thingsboard.server.service.telemetry.cmd.v2.LatestValueCmd; import org.thingsboard.server.service.ws.telemetry.cmd.v2.LatestValueCmd;
import org.thingsboard.server.service.telemetry.cmd.v2.TimeSeriesCmd; import org.thingsboard.server.service.ws.telemetry.cmd.v2.TimeSeriesCmd;
import org.thingsboard.server.service.telemetry.sub.TelemetrySubscriptionUpdate; import org.thingsboard.server.service.ws.telemetry.sub.TelemetrySubscriptionUpdate;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
@ -58,9 +58,9 @@ public class TbEntityDataSubCtx extends TbAbstractDataSubCtx<EntityDataQuery> {
private final int maxEntitiesPerDataSubscription; private final int maxEntitiesPerDataSubscription;
private Map<EntityId, Map<String, TsValue>> latestTsEntityData; private Map<EntityId, Map<String, TsValue>> latestTsEntityData;
public TbEntityDataSubCtx(String serviceId, TelemetryWebSocketService wsService, EntityService entityService, public TbEntityDataSubCtx(String serviceId, WebSocketService wsService, EntityService entityService,
TbLocalSubscriptionService localSubscriptionService, AttributesService attributesService, TbLocalSubscriptionService localSubscriptionService, AttributesService attributesService,
SubscriptionServiceStatistics stats, TelemetryWebSocketSessionRef sessionRef, int cmdId, int maxEntitiesPerDataSubscription) { SubscriptionServiceStatistics stats, WebSocketSessionRef sessionRef, int cmdId, int maxEntitiesPerDataSubscription) {
super(serviceId, wsService, entityService, localSubscriptionService, attributesService, stats, sessionRef, cmdId); super(serviceId, wsService, entityService, localSubscriptionService, attributesService, stats, sessionRef, cmdId);
this.maxEntitiesPerDataSubscription = maxEntitiesPerDataSubscription; this.maxEntitiesPerDataSubscription = maxEntitiesPerDataSubscription;
} }

17
application/src/main/java/org/thingsboard/server/service/subscription/TbEntityDataSubscriptionService.java

@ -15,20 +15,19 @@
*/ */
package org.thingsboard.server.service.subscription; package org.thingsboard.server.service.subscription;
import org.thingsboard.server.service.telemetry.TelemetryWebSocketSessionRef; import org.thingsboard.server.service.ws.WebSocketSessionRef;
import org.thingsboard.server.service.telemetry.cmd.v2.AlarmDataCmd; import org.thingsboard.server.service.ws.telemetry.cmd.v2.AlarmDataCmd;
import org.thingsboard.server.service.telemetry.cmd.v2.EntityCountCmd; import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityCountCmd;
import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataCmd; import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityDataCmd;
import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataUnsubscribeCmd; import org.thingsboard.server.service.ws.telemetry.cmd.v2.UnsubscribeCmd;
import org.thingsboard.server.service.telemetry.cmd.v2.UnsubscribeCmd;
public interface TbEntityDataSubscriptionService { public interface TbEntityDataSubscriptionService {
void handleCmd(TelemetryWebSocketSessionRef sessionId, EntityDataCmd cmd); void handleCmd(WebSocketSessionRef sessionId, EntityDataCmd cmd);
void handleCmd(TelemetryWebSocketSessionRef sessionId, EntityCountCmd cmd); void handleCmd(WebSocketSessionRef sessionId, EntityCountCmd cmd);
void handleCmd(TelemetryWebSocketSessionRef sessionId, AlarmDataCmd cmd); void handleCmd(WebSocketSessionRef sessionId, AlarmDataCmd cmd);
void cancelSubscription(String sessionId, UnsubscribeCmd subscriptionId); void cancelSubscription(String sessionId, UnsubscribeCmd subscriptionId);

7
application/src/main/java/org/thingsboard/server/service/subscription/TbLocalSubscriptionService.java

@ -18,8 +18,9 @@ package org.thingsboard.server.service.subscription;
import org.thingsboard.server.queue.discovery.event.ClusterTopologyChangeEvent; import org.thingsboard.server.queue.discovery.event.ClusterTopologyChangeEvent;
import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent; import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
import org.thingsboard.server.common.msg.queue.TbCallback; import org.thingsboard.server.common.msg.queue.TbCallback;
import org.thingsboard.server.service.telemetry.sub.AlarmSubscriptionUpdate; import org.thingsboard.server.service.ws.notification.sub.NotificationsSubscriptionUpdate;
import org.thingsboard.server.service.telemetry.sub.TelemetrySubscriptionUpdate; import org.thingsboard.server.service.ws.telemetry.sub.AlarmSubscriptionUpdate;
import org.thingsboard.server.service.ws.telemetry.sub.TelemetrySubscriptionUpdate;
public interface TbLocalSubscriptionService { public interface TbLocalSubscriptionService {
@ -33,6 +34,8 @@ public interface TbLocalSubscriptionService {
void onSubscriptionUpdate(String sessionId, AlarmSubscriptionUpdate update, TbCallback callback); void onSubscriptionUpdate(String sessionId, AlarmSubscriptionUpdate update, TbCallback callback);
void onSubscriptionUpdate(String sessionId, int subscriptionId, NotificationsSubscriptionUpdate update, TbCallback callback);
void onApplicationEvent(PartitionChangeEvent event); void onApplicationEvent(PartitionChangeEvent event);
void onApplicationEvent(ClusterTopologyChangeEvent event); void onApplicationEvent(ClusterTopologyChangeEvent event);

2
application/src/main/java/org/thingsboard/server/service/subscription/TbSubscription.java

@ -33,7 +33,7 @@ public abstract class TbSubscription<T> {
private final TenantId tenantId; private final TenantId tenantId;
private final EntityId entityId; private final EntityId entityId;
private final TbSubscriptionType type; private final TbSubscriptionType type;
private final BiConsumer<String, T> updateConsumer; private final BiConsumer<? extends TbSubscription<T>, T> updateProcessor;
@Override @Override
public boolean equals(Object o) { public boolean equals(Object o) {

2
application/src/main/java/org/thingsboard/server/service/subscription/TbSubscriptionType.java

@ -16,5 +16,5 @@
package org.thingsboard.server.service.subscription; package org.thingsboard.server.service.subscription;
public enum TbSubscriptionType { public enum TbSubscriptionType {
TIMESERIES, ATTRIBUTES, ALARMS TIMESERIES, ATTRIBUTES, ALARMS, NOTIFICATIONS
} }

71
application/src/main/java/org/thingsboard/server/service/subscription/TbSubscriptionUtils.java

@ -19,7 +19,9 @@ import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.Alarm;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.EntityIdFactory; import org.thingsboard.server.common.data.id.EntityIdFactory;
import org.thingsboard.server.common.data.id.NotificationRequestId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.kv.AttributeKvEntry; import org.thingsboard.server.common.data.kv.AttributeKvEntry;
import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry;
import org.thingsboard.server.common.data.kv.BasicTsKvEntry; import org.thingsboard.server.common.data.kv.BasicTsKvEntry;
@ -31,6 +33,7 @@ import org.thingsboard.server.common.data.kv.KvEntry;
import org.thingsboard.server.common.data.kv.LongDataEntry; import org.thingsboard.server.common.data.kv.LongDataEntry;
import org.thingsboard.server.common.data.kv.StringDataEntry; import org.thingsboard.server.common.data.kv.StringDataEntry;
import org.thingsboard.server.common.data.kv.TsKvEntry; import org.thingsboard.server.common.data.kv.TsKvEntry;
import org.thingsboard.server.common.data.notification.Notification;
import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.gen.transport.TransportProtos.KeyValueProto; import org.thingsboard.server.gen.transport.TransportProtos.KeyValueProto;
import org.thingsboard.server.gen.transport.TransportProtos.KeyValueType; import org.thingsboard.server.gen.transport.TransportProtos.KeyValueType;
@ -50,9 +53,10 @@ import org.thingsboard.server.gen.transport.TransportProtos.TbTimeSeriesSubscrip
import org.thingsboard.server.gen.transport.TransportProtos.TbTimeSeriesUpdateProto; import org.thingsboard.server.gen.transport.TransportProtos.TbTimeSeriesUpdateProto;
import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg;
import org.thingsboard.server.gen.transport.TransportProtos.TsKvProto; import org.thingsboard.server.gen.transport.TransportProtos.TsKvProto;
import org.thingsboard.server.service.telemetry.sub.AlarmSubscriptionUpdate; import org.thingsboard.server.service.ws.notification.sub.NotificationsSubscriptionUpdate;
import org.thingsboard.server.service.telemetry.sub.SubscriptionErrorCode; import org.thingsboard.server.service.ws.notification.sub.NotificationsSubscription;
import org.thingsboard.server.service.telemetry.sub.TelemetrySubscriptionUpdate; import org.thingsboard.server.service.ws.telemetry.sub.AlarmSubscriptionUpdate;
import org.thingsboard.server.service.ws.telemetry.sub.TelemetrySubscriptionUpdate;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
@ -105,6 +109,12 @@ public class TbSubscriptionUtils {
.setTs(alarmSub.getTs()); .setTs(alarmSub.getTs());
msgBuilder.setAlarmSub(alarmSubProto.build()); msgBuilder.setAlarmSub(alarmSubProto.build());
break; break;
case NOTIFICATIONS:
NotificationsSubscription notificationsSub = (NotificationsSubscription) subscription;
msgBuilder.setNotificationsSub(TransportProtos.NotificationsSubscriptionProto.newBuilder()
.setSub(subscriptionProto)
.setLimit(notificationsSub.getLimit()));
break;
} }
return ToCoreMsg.newBuilder().setToSubscriptionMgrMsg(msgBuilder.build()).build(); return ToCoreMsg.newBuilder().setToSubscriptionMgrMsg(msgBuilder.build()).build();
} }
@ -166,6 +176,18 @@ public class TbSubscriptionUtils {
return builder.build(); return builder.build();
} }
public static NotificationsSubscription fromProto(TransportProtos.NotificationsSubscriptionProto notificationsSub) {
TbSubscriptionProto sub = notificationsSub.getSub();
return NotificationsSubscription.builder()
.serviceId(sub.getServiceId())
.sessionId(sub.getSessionId())
.subscriptionId(sub.getSubscriptionId())
.tenantId(TenantId.fromUUID(new UUID(sub.getTenantIdMSB(), sub.getTenantIdLSB())))
.entityId(EntityIdFactory.getByTypeAndUuid(sub.getEntityType(), new UUID(sub.getEntityIdMSB(), sub.getEntityIdLSB())))
.limit(notificationsSub.getLimit())
.build();
}
public static TelemetrySubscriptionUpdate fromProto(TbSubscriptionUpdateProto proto) { public static TelemetrySubscriptionUpdate fromProto(TbSubscriptionUpdateProto proto) {
if (proto.getErrorCode() > 0) { if (proto.getErrorCode() > 0) {
return new TelemetrySubscriptionUpdate(proto.getSubscriptionId(), SubscriptionErrorCode.forCode(proto.getErrorCode()), proto.getErrorMsg()); return new TelemetrySubscriptionUpdate(proto.getSubscriptionId(), SubscriptionErrorCode.forCode(proto.getErrorCode()), proto.getErrorMsg());
@ -340,4 +362,47 @@ public class TbSubscriptionUtils {
msgBuilder.setAlarmDelete(builder); msgBuilder.setAlarmDelete(builder);
return ToCoreMsg.newBuilder().setToSubscriptionMgrMsg(msgBuilder.build()).build(); return ToCoreMsg.newBuilder().setToSubscriptionMgrMsg(msgBuilder.build()).build();
} }
public static TransportProtos.ToCoreNotificationMsg notificationsSubUpdateToProto(TbSubscription subscription, NotificationsSubscriptionUpdate update) {
TransportProtos.NotificationsSubscriptionUpdateProto updateProto = TransportProtos.NotificationsSubscriptionUpdateProto.newBuilder()
.setSessionId(subscription.getSessionId())
.setSubscriptionId(subscription.getSubscriptionId())
.setNotification(JacksonUtil.toString(update.getNotification()))
.build();
return TransportProtos.ToCoreNotificationMsg.newBuilder()
.setToLocalSubscriptionServiceMsg(TransportProtos.LocalSubscriptionServiceMsgProto.newBuilder()
.setNotificationsSubUpdate(updateProto)
.build())
.build();
}
public static ToCoreMsg notificationUpdateToProto(TenantId tenantId, UserId recipientId, Notification notification) {
TransportProtos.NotificationUpdateProto updateProto = TransportProtos.NotificationUpdateProto.newBuilder()
.setTenantIdMSB(tenantId.getId().getMostSignificantBits())
.setTenantIdLSB(tenantId.getId().getLeastSignificantBits())
.setRecipientIdMSB(recipientId.getId().getMostSignificantBits())
.setRecipientIdLSB(recipientId.getId().getLeastSignificantBits())
.setNotification(JacksonUtil.toString(notification))
.build();
return ToCoreMsg.newBuilder()
.setToSubscriptionMgrMsg(SubscriptionMgrMsgProto.newBuilder()
.setNotificationUpdate(updateProto)
.build())
.build();
}
public static ToCoreMsg notificationRequestDeletedToProto(TenantId tenantId, NotificationRequestId notificationRequestId) {
TransportProtos.NotificationRequestDeleteProto deleteProto = TransportProtos.NotificationRequestDeleteProto.newBuilder()
.setTenantIdMSB(tenantId.getId().getMostSignificantBits())
.setTenantIdLSB(tenantId.getId().getLeastSignificantBits())
.setNotificationRequestIdMSB(notificationRequestId.getId().getMostSignificantBits())
.setNotificationRequestIdLSB(notificationRequestId.getId().getLeastSignificantBits())
.build();
return ToCoreMsg.newBuilder()
.setToSubscriptionMgrMsg(SubscriptionMgrMsgProto.newBuilder()
.setNotificationRequestDelete(deleteProto)
.build())
.build();
}
} }

6
application/src/main/java/org/thingsboard/server/service/subscription/TbTimeseriesSubscription.java

@ -19,7 +19,7 @@ import lombok.Builder;
import lombok.Getter; import lombok.Getter;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.service.telemetry.sub.TelemetrySubscriptionUpdate; import org.thingsboard.server.service.ws.telemetry.sub.TelemetrySubscriptionUpdate;
import java.util.Map; import java.util.Map;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
@ -39,9 +39,9 @@ public class TbTimeseriesSubscription extends TbSubscription<TelemetrySubscripti
@Builder @Builder
public TbTimeseriesSubscription(String serviceId, String sessionId, int subscriptionId, TenantId tenantId, EntityId entityId, public TbTimeseriesSubscription(String serviceId, String sessionId, int subscriptionId, TenantId tenantId, EntityId entityId,
BiConsumer<String, TelemetrySubscriptionUpdate> updateConsumer, BiConsumer<TbSubscription<TelemetrySubscriptionUpdate>, TelemetrySubscriptionUpdate> updateProcessor,
boolean allKeys, Map<String, Long> keyStates, long startTime, long endTime, boolean latestValues) { boolean allKeys, Map<String, Long> keyStates, long startTime, long endTime, boolean latestValues) {
super(serviceId, sessionId, subscriptionId, tenantId, entityId, TbSubscriptionType.TIMESERIES, updateConsumer); super(serviceId, sessionId, subscriptionId, tenantId, entityId, TbSubscriptionType.TIMESERIES, updateProcessor);
this.allKeys = allKeys; this.allKeys = allKeys;
this.keyStates = keyStates; this.keyStates = keyStates;
this.startTime = startTime; this.startTime = startTime;

22
application/src/main/java/org/thingsboard/server/service/telemetry/AbstractSubscriptionService.java

@ -21,8 +21,11 @@ import com.google.common.util.concurrent.ListenableFuture;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.thingsboard.common.util.ThingsBoardThreadFactory; import org.thingsboard.common.util.ThingsBoardThreadFactory;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.common.msg.queue.ServiceType;
import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent; import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
import org.thingsboard.server.queue.discovery.PartitionService; import org.thingsboard.server.queue.discovery.PartitionService;
import org.thingsboard.server.queue.discovery.TbApplicationEventListener; import org.thingsboard.server.queue.discovery.TbApplicationEventListener;
@ -38,12 +41,13 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Supplier;
/** /**
* Created by ashvayka on 27.03.18. * Created by ashvayka on 27.03.18.
*/ */
@Slf4j @Slf4j
public abstract class AbstractSubscriptionService extends TbApplicationEventListener<PartitionChangeEvent>{ public abstract class AbstractSubscriptionService extends TbApplicationEventListener<PartitionChangeEvent> {
protected final Set<TopicPartitionInfo> currentPartitions = ConcurrentHashMap.newKeySet(); protected final Set<TopicPartitionInfo> currentPartitions = ConcurrentHashMap.newKeySet();
@ -86,6 +90,22 @@ public abstract class AbstractSubscriptionService extends TbApplicationEventList
} }
} }
protected void forwardToSubscriptionManagerServiceOrSendToCore(TenantId tenantId, EntityId entityId,
Consumer<SubscriptionManagerService> toSubscriptionManagerService,
Supplier<TransportProtos.ToCoreMsg> toCore) {
TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_CORE, tenantId, entityId);
if (currentPartitions.contains(tpi)) {
if (subscriptionManagerService.isPresent()) {
toSubscriptionManagerService.accept(subscriptionManagerService.get());
} else {
log.warn("Possible misconfiguration because subscriptionManagerService is null!");
}
} else {
TransportProtos.ToCoreMsg toCoreMsg = toCore.get();
clusterService.pushMsgToCore(tpi, entityId.getId(), toCoreMsg, null);
}
}
protected <T> void addWsCallback(ListenableFuture<T> saveFuture, Consumer<T> callback) { protected <T> void addWsCallback(ListenableFuture<T> saveFuture, Consumer<T> callback) {
Futures.addCallback(saveFuture, new FutureCallback<T>() { Futures.addCallback(saveFuture, new FutureCallback<T>() {
@Override @Override

32
application/src/main/java/org/thingsboard/server/service/telemetry/DefaultAlarmSubscriptionService.java

@ -164,17 +164,11 @@ public class DefaultAlarmSubscriptionService extends AbstractSubscriptionService
Alarm alarm = result.getAlarm(); Alarm alarm = result.getAlarm();
TenantId tenantId = result.getAlarm().getTenantId(); TenantId tenantId = result.getAlarm().getTenantId();
for (EntityId entityId : result.getPropagatedEntitiesList()) { for (EntityId entityId : result.getPropagatedEntitiesList()) {
TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_CORE, tenantId, entityId); forwardToSubscriptionManagerServiceOrSendToCore(tenantId, entityId, subscriptionManagerService -> {
if (currentPartitions.contains(tpi)) { subscriptionManagerService.onAlarmUpdate(tenantId, entityId, alarm, TbCallback.EMPTY);
if (subscriptionManagerService.isPresent()) { }, () -> {
subscriptionManagerService.get().onAlarmUpdate(tenantId, entityId, alarm, TbCallback.EMPTY); return TbSubscriptionUtils.toAlarmUpdateProto(tenantId, entityId, alarm);
} else { });
log.warn("Possible misconfiguration because subscriptionManagerService is null!");
}
} else {
TransportProtos.ToCoreMsg toCoreMsg = TbSubscriptionUtils.toAlarmUpdateProto(tenantId, entityId, alarm);
clusterService.pushMsgToCore(tpi, entityId.getId(), toCoreMsg, null);
}
} }
}); });
} }
@ -184,17 +178,11 @@ public class DefaultAlarmSubscriptionService extends AbstractSubscriptionService
Alarm alarm = result.getAlarm(); Alarm alarm = result.getAlarm();
TenantId tenantId = result.getAlarm().getTenantId(); TenantId tenantId = result.getAlarm().getTenantId();
for (EntityId entityId : result.getPropagatedEntitiesList()) { for (EntityId entityId : result.getPropagatedEntitiesList()) {
TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_CORE, tenantId, entityId); forwardToSubscriptionManagerServiceOrSendToCore(tenantId, entityId, subscriptionManagerService -> {
if (currentPartitions.contains(tpi)) { subscriptionManagerService.onAlarmDeleted(tenantId, entityId, alarm, TbCallback.EMPTY);
if (subscriptionManagerService.isPresent()) { }, () -> {
subscriptionManagerService.get().onAlarmDeleted(tenantId, entityId, alarm, TbCallback.EMPTY); return TbSubscriptionUtils.toAlarmDeletedProto(tenantId, entityId, alarm);
} else { });
log.warn("Possible misconfiguration because subscriptionManagerService is null!");
}
} else {
TransportProtos.ToCoreMsg toCoreMsg = TbSubscriptionUtils.toAlarmDeletedProto(tenantId, entityId, alarm);
clusterService.pushMsgToCore(tpi, entityId.getId(), toCoreMsg, null);
}
} }
}); });
} }

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

@ -40,12 +40,9 @@ import org.thingsboard.server.common.data.kv.LongDataEntry;
import org.thingsboard.server.common.data.kv.StringDataEntry; import org.thingsboard.server.common.data.kv.StringDataEntry;
import org.thingsboard.server.common.data.kv.TsKvEntry; import org.thingsboard.server.common.data.kv.TsKvEntry;
import org.thingsboard.server.common.data.kv.TsKvLatestRemovingResult; import org.thingsboard.server.common.data.kv.TsKvLatestRemovingResult;
import org.thingsboard.server.common.msg.queue.ServiceType;
import org.thingsboard.server.common.msg.queue.TbCallback; import org.thingsboard.server.common.msg.queue.TbCallback;
import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
import org.thingsboard.server.dao.attributes.AttributesService; import org.thingsboard.server.dao.attributes.AttributesService;
import org.thingsboard.server.dao.timeseries.TimeseriesService; import org.thingsboard.server.dao.timeseries.TimeseriesService;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.queue.discovery.PartitionService; import org.thingsboard.server.queue.discovery.PartitionService;
import org.thingsboard.server.queue.usagestats.TbApiUsageClient; import org.thingsboard.server.queue.usagestats.TbApiUsageClient;
import org.thingsboard.server.service.apiusage.TbApiUsageStateService; import org.thingsboard.server.service.apiusage.TbApiUsageStateService;
@ -369,73 +366,49 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer
} }
private void onAttributesUpdate(TenantId tenantId, EntityId entityId, String scope, List<AttributeKvEntry> attributes, boolean notifyDevice) { private void onAttributesUpdate(TenantId tenantId, EntityId entityId, String scope, List<AttributeKvEntry> attributes, boolean notifyDevice) {
TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_CORE, tenantId, entityId); forwardToSubscriptionManagerServiceOrSendToCore(tenantId, entityId, subscriptionManagerService -> {
if (currentPartitions.contains(tpi)) { subscriptionManagerService.onAttributesUpdate(tenantId, entityId, scope, attributes, notifyDevice, TbCallback.EMPTY);
if (subscriptionManagerService.isPresent()) { }, () -> {
subscriptionManagerService.get().onAttributesUpdate(tenantId, entityId, scope, attributes, notifyDevice, TbCallback.EMPTY); return TbSubscriptionUtils.toAttributesUpdateProto(tenantId, entityId, scope, attributes);
} else { });
log.warn("Possible misconfiguration because subscriptionManagerService is null!");
}
} else {
TransportProtos.ToCoreMsg toCoreMsg = TbSubscriptionUtils.toAttributesUpdateProto(tenantId, entityId, scope, attributes);
clusterService.pushMsgToCore(tpi, entityId.getId(), toCoreMsg, null);
}
} }
private void onAttributesDelete(TenantId tenantId, EntityId entityId, String scope, List<String> keys) { private void onAttributesDelete(TenantId tenantId, EntityId entityId, String scope, List<String> keys) {
TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_CORE, tenantId, entityId); forwardToSubscriptionManagerServiceOrSendToCore(tenantId, entityId, subscriptionManagerService -> {
if (currentPartitions.contains(tpi)) { subscriptionManagerService.onAttributesDelete(tenantId, entityId, scope, keys, TbCallback.EMPTY);
if (subscriptionManagerService.isPresent()) { }, () -> {
subscriptionManagerService.get().onAttributesDelete(tenantId, entityId, scope, keys, TbCallback.EMPTY); return TbSubscriptionUtils.toAttributesDeleteProto(tenantId, entityId, scope, keys);
} else { });
log.warn("Possible misconfiguration because subscriptionManagerService is null!");
}
} else {
TransportProtos.ToCoreMsg toCoreMsg = TbSubscriptionUtils.toAttributesDeleteProto(tenantId, entityId, scope, keys);
clusterService.pushMsgToCore(tpi, entityId.getId(), toCoreMsg, null);
}
} }
private void onTimeSeriesUpdate(TenantId tenantId, EntityId entityId, List<TsKvEntry> ts) { private void onTimeSeriesUpdate(TenantId tenantId, EntityId entityId, List<TsKvEntry> ts) {
TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_CORE, tenantId, entityId); forwardToSubscriptionManagerServiceOrSendToCore(tenantId, entityId, subscriptionManagerService -> {
if (currentPartitions.contains(tpi)) { subscriptionManagerService.onTimeSeriesUpdate(tenantId, entityId, ts, TbCallback.EMPTY);
if (subscriptionManagerService.isPresent()) { }, () -> {
subscriptionManagerService.get().onTimeSeriesUpdate(tenantId, entityId, ts, TbCallback.EMPTY); return TbSubscriptionUtils.toTimeseriesUpdateProto(tenantId, entityId, ts);
} else { });
log.warn("Possible misconfiguration because subscriptionManagerService is null!");
}
} else {
TransportProtos.ToCoreMsg toCoreMsg = TbSubscriptionUtils.toTimeseriesUpdateProto(tenantId, entityId, ts);
clusterService.pushMsgToCore(tpi, entityId.getId(), toCoreMsg, null);
}
} }
private void onTimeSeriesDelete(TenantId tenantId, EntityId entityId, List<String> keys, List<TsKvLatestRemovingResult> ts) { private void onTimeSeriesDelete(TenantId tenantId, EntityId entityId, List<String> keys, List<TsKvLatestRemovingResult> ts) {
TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_CORE, tenantId, entityId); forwardToSubscriptionManagerServiceOrSendToCore(tenantId, entityId, subscriptionManagerService -> {
if (currentPartitions.contains(tpi)) { List<TsKvEntry> updated = new ArrayList<>();
if (subscriptionManagerService.isPresent()) { List<String> deleted = new ArrayList<>();
List<TsKvEntry> updated = new ArrayList<>();
List<String> deleted = new ArrayList<>(); ts.stream().filter(Objects::nonNull).forEach(res -> {
if (res.isRemoved()) {
ts.stream().filter(Objects::nonNull).forEach(res -> { if (res.getData() != null) {
if (res.isRemoved()) { updated.add(res.getData());
if (res.getData() != null) { } else {
updated.add(res.getData()); deleted.add(res.getKey());
} else {
deleted.add(res.getKey());
}
} }
}); }
});
subscriptionManagerService.get().onTimeSeriesUpdate(tenantId, entityId, updated, TbCallback.EMPTY); subscriptionManagerService.onTimeSeriesUpdate(tenantId, entityId, updated, TbCallback.EMPTY);
subscriptionManagerService.get().onTimeSeriesDelete(tenantId, entityId, deleted, TbCallback.EMPTY); subscriptionManagerService.onTimeSeriesDelete(tenantId, entityId, deleted, TbCallback.EMPTY);
} else { }, () -> {
log.warn("Possible misconfiguration because subscriptionManagerService is null!"); return TbSubscriptionUtils.toTimeseriesDeleteProto(tenantId, entityId, keys);
} });
} else {
TransportProtos.ToCoreMsg toCoreMsg = TbSubscriptionUtils.toTimeseriesDeleteProto(tenantId, entityId, keys);
clusterService.pushMsgToCore(tpi, entityId.getId(), toCoreMsg, null);
}
} }
private <S> void addVoidCallback(ListenableFuture<S> saveFuture, final FutureCallback<Void> callback) { private <S> void addVoidCallback(ListenableFuture<S> saveFuture, final FutureCallback<Void> callback) {

225
application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetryWebSocketService.java → application/src/main/java/org/thingsboard/server/service/telemetry/DefaultWebSocketService.java

@ -47,6 +47,7 @@ import org.thingsboard.server.dao.attributes.AttributesService;
import org.thingsboard.server.dao.tenant.TbTenantProfileCache; import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
import org.thingsboard.server.dao.timeseries.TimeseriesService; import org.thingsboard.server.dao.timeseries.TimeseriesService;
import org.thingsboard.server.dao.util.TenantRateLimitException; import org.thingsboard.server.dao.util.TenantRateLimitException;
import org.thingsboard.server.exception.UnauthorizedException;
import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; import org.thingsboard.server.queue.discovery.TbServiceInfoProvider;
import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.security.AccessValidator; import org.thingsboard.server.service.security.AccessValidator;
@ -55,26 +56,35 @@ import org.thingsboard.server.service.security.ValidationResult;
import org.thingsboard.server.service.security.ValidationResultCode; import org.thingsboard.server.service.security.ValidationResultCode;
import org.thingsboard.server.service.security.model.UserPrincipal; import org.thingsboard.server.service.security.model.UserPrincipal;
import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.security.permission.Operation;
import org.thingsboard.server.service.subscription.SubscriptionErrorCode;
import org.thingsboard.server.service.subscription.TbAttributeSubscription; import org.thingsboard.server.service.subscription.TbAttributeSubscription;
import org.thingsboard.server.service.subscription.TbAttributeSubscriptionScope; import org.thingsboard.server.service.subscription.TbAttributeSubscriptionScope;
import org.thingsboard.server.service.subscription.TbEntityDataSubscriptionService; import org.thingsboard.server.service.subscription.TbEntityDataSubscriptionService;
import org.thingsboard.server.service.subscription.TbLocalSubscriptionService; import org.thingsboard.server.service.subscription.TbLocalSubscriptionService;
import org.thingsboard.server.service.subscription.TbTimeseriesSubscription; import org.thingsboard.server.service.subscription.TbTimeseriesSubscription;
import org.thingsboard.server.service.telemetry.cmd.TelemetryPluginCmdsWrapper; import org.thingsboard.server.service.ws.SessionEvent;
import org.thingsboard.server.service.telemetry.cmd.v1.AttributesSubscriptionCmd; import org.thingsboard.server.service.ws.WebSocketMsgEndpoint;
import org.thingsboard.server.service.telemetry.cmd.v1.GetHistoryCmd; import org.thingsboard.server.service.ws.WebSocketSessionRef;
import org.thingsboard.server.service.telemetry.cmd.v1.SubscriptionCmd; import org.thingsboard.server.service.ws.WsSessionMetaData;
import org.thingsboard.server.service.telemetry.cmd.v1.TelemetryPluginCmd; import org.thingsboard.server.service.ws.notification.cmd.NotificationCmdsWrapper;
import org.thingsboard.server.service.telemetry.cmd.v1.TimeseriesSubscriptionCmd; import org.thingsboard.server.service.ws.notification.cmd.MarkNotificationAsReadCmd;
import org.thingsboard.server.service.telemetry.cmd.v2.AlarmDataCmd; import org.thingsboard.server.service.ws.notification.cmd.NotificationsSubCmd;
import org.thingsboard.server.service.telemetry.cmd.v2.CmdUpdate; import org.thingsboard.server.service.ws.notification.cmd.NotificationsUnsubCmd;
import org.thingsboard.server.service.telemetry.cmd.v2.EntityCountCmd; import org.thingsboard.server.service.ws.notification.sub.DefaultNotificationsSubscriptionService;
import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataCmd; import org.thingsboard.server.service.ws.telemetry.WebSocketService;
import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataUpdate; import org.thingsboard.server.service.ws.telemetry.cmd.TelemetryPluginCmdsWrapper;
import org.thingsboard.server.service.telemetry.cmd.v2.UnsubscribeCmd; import org.thingsboard.server.service.ws.telemetry.cmd.v1.AttributesSubscriptionCmd;
import org.thingsboard.server.service.telemetry.exception.UnauthorizedException; import org.thingsboard.server.service.ws.telemetry.cmd.v1.GetHistoryCmd;
import org.thingsboard.server.service.telemetry.sub.SubscriptionErrorCode; import org.thingsboard.server.service.ws.telemetry.cmd.v1.SubscriptionCmd;
import org.thingsboard.server.service.telemetry.sub.TelemetrySubscriptionUpdate; import org.thingsboard.server.service.ws.telemetry.cmd.v1.TelemetryPluginCmd;
import org.thingsboard.server.service.ws.telemetry.cmd.v1.TimeseriesSubscriptionCmd;
import org.thingsboard.server.service.ws.telemetry.cmd.v2.AlarmDataCmd;
import org.thingsboard.server.service.ws.telemetry.cmd.v2.CmdUpdate;
import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityCountCmd;
import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityDataCmd;
import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityDataUpdate;
import org.thingsboard.server.service.ws.telemetry.cmd.v2.UnsubscribeCmd;
import org.thingsboard.server.service.ws.telemetry.sub.TelemetrySubscriptionUpdate;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
@ -103,7 +113,7 @@ import java.util.stream.Collectors;
@Service @Service
@TbCoreComponent @TbCoreComponent
@Slf4j @Slf4j
public class DefaultTelemetryWebSocketService implements TelemetryWebSocketService { public class DefaultWebSocketService implements WebSocketService {
public static final int NUMBER_OF_PING_ATTEMPTS = 3; public static final int NUMBER_OF_PING_ATTEMPTS = 3;
@ -126,7 +136,10 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi
private TbEntityDataSubscriptionService entityDataSubService; private TbEntityDataSubscriptionService entityDataSubService;
@Autowired @Autowired
private TelemetryWebSocketMsgEndpoint msgEndpoint; private DefaultNotificationsSubscriptionService notificationsSubService;
@Autowired
private WebSocketMsgEndpoint msgEndpoint;
@Autowired @Autowired
private AccessValidator accessValidator; private AccessValidator accessValidator;
@ -177,7 +190,7 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi
} }
@Override @Override
public void handleWebSocketSessionEvent(TelemetryWebSocketSessionRef sessionRef, SessionEvent event) { public void handleWebSocketSessionEvent(WebSocketSessionRef sessionRef, SessionEvent event) {
String sessionId = sessionRef.getSessionId(); String sessionId = sessionRef.getSessionId();
log.debug(PROCESSING_MSG, sessionId, event); log.debug(PROCESSING_MSG, sessionId, event);
switch (event.getEventType()) { switch (event.getEventType()) {
@ -197,49 +210,19 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi
} }
@Override @Override
public void handleWebSocketMsg(TelemetryWebSocketSessionRef sessionRef, String msg) { public void handleWebSocketMsg(WebSocketSessionRef sessionRef, String msg) {
if (log.isTraceEnabled()) { if (log.isTraceEnabled()) {
log.trace("[{}] Processing: {}", sessionRef.getSessionId(), msg); log.trace("[{}] Processing: {}", sessionRef.getSessionId(), msg);
} }
try { try {
TelemetryPluginCmdsWrapper cmdsWrapper = jsonMapper.readValue(msg, TelemetryPluginCmdsWrapper.class); switch (sessionRef.getSessionType()) {
if (cmdsWrapper != null) { case TELEMETRY:
if (cmdsWrapper.getAttrSubCmds() != null) { processTelemetryCmds(sessionRef, msg);
cmdsWrapper.getAttrSubCmds().forEach(cmd -> { break;
if (processSubscription(sessionRef, cmd)) { case NOTIFICATIONS:
handleWsAttributesSubscriptionCmd(sessionRef, cmd); processNotificationCmds(sessionRef, msg);
} break;
});
}
if (cmdsWrapper.getTsSubCmds() != null) {
cmdsWrapper.getTsSubCmds().forEach(cmd -> {
if (processSubscription(sessionRef, cmd)) {
handleWsTimeseriesSubscriptionCmd(sessionRef, cmd);
}
});
}
if (cmdsWrapper.getHistoryCmds() != null) {
cmdsWrapper.getHistoryCmds().forEach(cmd -> handleWsHistoryCmd(sessionRef, cmd));
}
if (cmdsWrapper.getEntityDataCmds() != null) {
cmdsWrapper.getEntityDataCmds().forEach(cmd -> handleWsEntityDataCmd(sessionRef, cmd));
}
if (cmdsWrapper.getAlarmDataCmds() != null) {
cmdsWrapper.getAlarmDataCmds().forEach(cmd -> handleWsAlarmDataCmd(sessionRef, cmd));
}
if (cmdsWrapper.getEntityCountCmds() != null) {
cmdsWrapper.getEntityCountCmds().forEach(cmd -> handleWsEntityCountCmd(sessionRef, cmd));
}
if (cmdsWrapper.getEntityDataUnsubscribeCmds() != null) {
cmdsWrapper.getEntityDataUnsubscribeCmds().forEach(cmd -> handleWsDataUnsubscribeCmd(sessionRef, cmd));
}
if (cmdsWrapper.getAlarmDataUnsubscribeCmds() != null) {
cmdsWrapper.getAlarmDataUnsubscribeCmds().forEach(cmd -> handleWsDataUnsubscribeCmd(sessionRef, cmd));
}
if (cmdsWrapper.getEntityCountUnsubscribeCmds() != null) {
cmdsWrapper.getEntityCountUnsubscribeCmds().forEach(cmd -> handleWsDataUnsubscribeCmd(sessionRef, cmd));
}
} }
} catch (IOException e) { } catch (IOException e) {
log.warn("Failed to decode subscription cmd: {}", e.getMessage(), e); log.warn("Failed to decode subscription cmd: {}", e.getMessage(), e);
@ -247,7 +230,81 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi
} }
} }
private void handleWsEntityDataCmd(TelemetryWebSocketSessionRef sessionRef, EntityDataCmd cmd) { private void processTelemetryCmds(WebSocketSessionRef sessionRef, String msg) throws JsonProcessingException {
TelemetryPluginCmdsWrapper cmdsWrapper = jsonMapper.readValue(msg, TelemetryPluginCmdsWrapper.class);
if (cmdsWrapper == null) {
return;
}
if (cmdsWrapper.getAttrSubCmds() != null) {
cmdsWrapper.getAttrSubCmds().forEach(cmd -> {
if (processSubscription(sessionRef, cmd)) {
handleWsAttributesSubscriptionCmd(sessionRef, cmd);
}
});
}
if (cmdsWrapper.getTsSubCmds() != null) {
cmdsWrapper.getTsSubCmds().forEach(cmd -> {
if (processSubscription(sessionRef, cmd)) {
handleWsTimeseriesSubscriptionCmd(sessionRef, cmd);
}
});
}
if (cmdsWrapper.getHistoryCmds() != null) {
cmdsWrapper.getHistoryCmds().forEach(cmd -> handleWsHistoryCmd(sessionRef, cmd));
}
if (cmdsWrapper.getEntityDataCmds() != null) {
cmdsWrapper.getEntityDataCmds().forEach(cmd -> handleWsEntityDataCmd(sessionRef, cmd));
}
if (cmdsWrapper.getAlarmDataCmds() != null) {
cmdsWrapper.getAlarmDataCmds().forEach(cmd -> handleWsAlarmDataCmd(sessionRef, cmd));
}
if (cmdsWrapper.getEntityCountCmds() != null) {
cmdsWrapper.getEntityCountCmds().forEach(cmd -> handleWsEntityCountCmd(sessionRef, cmd));
}
if (cmdsWrapper.getEntityDataUnsubscribeCmds() != null) {
cmdsWrapper.getEntityDataUnsubscribeCmds().forEach(cmd -> handleWsDataUnsubscribeCmd(sessionRef, cmd));
}
if (cmdsWrapper.getAlarmDataUnsubscribeCmds() != null) {
cmdsWrapper.getAlarmDataUnsubscribeCmds().forEach(cmd -> handleWsDataUnsubscribeCmd(sessionRef, cmd));
}
if (cmdsWrapper.getEntityCountUnsubscribeCmds() != null) {
cmdsWrapper.getEntityCountUnsubscribeCmds().forEach(cmd -> handleWsDataUnsubscribeCmd(sessionRef, cmd));
}
}
private void processNotificationCmds(WebSocketSessionRef sessionRef, String msg) throws IOException {
NotificationCmdsWrapper cmdsWrapper = jsonMapper.readValue(msg, NotificationCmdsWrapper.class);
if (cmdsWrapper.getUnreadSubCmd() != null) {
handleUnreadNotificationsSubCmd(sessionRef, cmdsWrapper.getUnreadSubCmd());
} else if (cmdsWrapper.getUnreadUnsubCmd() != null) {
handleUnreadNotificationsUnsubCmd(sessionRef, cmdsWrapper.getUnreadUnsubCmd());
} else if (cmdsWrapper.getMarkAsReadCmd() != null) {
handleMarkNotificationAsReadCmd(sessionRef, cmdsWrapper.getMarkAsReadCmd());
}
}
private void handleUnreadNotificationsSubCmd(WebSocketSessionRef sessionRef, NotificationsSubCmd cmd) {
String sessionId = sessionRef.getSessionId();
if (validateSessionMetadata(sessionRef, cmd.getCmdId(), sessionId)) {
notificationsSubService.handleUnreadNotificationsSubCmd(sessionRef, cmd);
}
}
private void handleUnreadNotificationsUnsubCmd(WebSocketSessionRef sessionRef, NotificationsUnsubCmd cmd) {
String sessionId = sessionRef.getSessionId();
if (validateSessionMetadata(sessionRef, cmd.getCmdId(), sessionId)) {
notificationsSubService.handleUnsubCmd(sessionRef, cmd);
}
}
private void handleMarkNotificationAsReadCmd(WebSocketSessionRef sessionRef, MarkNotificationAsReadCmd cmd) {
String sessionId = sessionRef.getSessionId();
if (validateSessionMetadata(sessionRef, cmd.getCmdId(), sessionId)) {
notificationsSubService.handleMarkAsReadCmd(sessionRef, cmd);
}
}
private void handleWsEntityDataCmd(WebSocketSessionRef sessionRef, EntityDataCmd cmd) {
String sessionId = sessionRef.getSessionId(); String sessionId = sessionRef.getSessionId();
log.debug("[{}] Processing: {}", sessionId, cmd); log.debug("[{}] Processing: {}", sessionId, cmd);
@ -257,7 +314,7 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi
} }
} }
private void handleWsEntityCountCmd(TelemetryWebSocketSessionRef sessionRef, EntityCountCmd cmd) { private void handleWsEntityCountCmd(WebSocketSessionRef sessionRef, EntityCountCmd cmd) {
String sessionId = sessionRef.getSessionId(); String sessionId = sessionRef.getSessionId();
log.debug("[{}] Processing: {}", sessionId, cmd); log.debug("[{}] Processing: {}", sessionId, cmd);
@ -267,7 +324,7 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi
} }
} }
private void handleWsAlarmDataCmd(TelemetryWebSocketSessionRef sessionRef, AlarmDataCmd cmd) { private void handleWsAlarmDataCmd(WebSocketSessionRef sessionRef, AlarmDataCmd cmd) {
String sessionId = sessionRef.getSessionId(); String sessionId = sessionRef.getSessionId();
log.debug("[{}] Processing: {}", sessionId, cmd); log.debug("[{}] Processing: {}", sessionId, cmd);
@ -277,7 +334,7 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi
} }
} }
private void handleWsDataUnsubscribeCmd(TelemetryWebSocketSessionRef sessionRef, UnsubscribeCmd cmd) { private void handleWsDataUnsubscribeCmd(WebSocketSessionRef sessionRef, UnsubscribeCmd cmd) {
String sessionId = sessionRef.getSessionId(); String sessionId = sessionRef.getSessionId();
log.debug("[{}] Processing: {}", sessionId, cmd); log.debug("[{}] Processing: {}", sessionId, cmd);
@ -315,7 +372,7 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi
} }
} }
private void processSessionClose(TelemetryWebSocketSessionRef sessionRef) { private void processSessionClose(WebSocketSessionRef sessionRef) {
var tenantProfileConfiguration = tenantProfileCache.get(sessionRef.getSecurityCtx().getTenantId()).getDefaultProfileConfiguration(); var tenantProfileConfiguration = tenantProfileCache.get(sessionRef.getSecurityCtx().getTenantId()).getDefaultProfileConfiguration();
if (tenantProfileConfiguration != null) { if (tenantProfileConfiguration != null) {
String sessionId = "[" + sessionRef.getSessionId() + "]"; String sessionId = "[" + sessionRef.getSessionId() + "]";
@ -349,7 +406,7 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi
} }
} }
private boolean processSubscription(TelemetryWebSocketSessionRef sessionRef, SubscriptionCmd cmd) { private boolean processSubscription(WebSocketSessionRef sessionRef, SubscriptionCmd cmd) {
var tenantProfileConfiguration = (DefaultTenantProfileConfiguration) tenantProfileCache.get(sessionRef.getSecurityCtx().getTenantId()).getDefaultProfileConfiguration(); var tenantProfileConfiguration = (DefaultTenantProfileConfiguration) tenantProfileCache.get(sessionRef.getSecurityCtx().getTenantId()).getDefaultProfileConfiguration();
String subId = "[" + sessionRef.getSessionId() + "]:[" + cmd.getCmdId() + "]"; String subId = "[" + sessionRef.getSessionId() + "]:[" + cmd.getCmdId() + "]";
@ -420,7 +477,7 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi
return true; return true;
} }
private void handleWsAttributesSubscriptionCmd(TelemetryWebSocketSessionRef sessionRef, AttributesSubscriptionCmd cmd) { private void handleWsAttributesSubscriptionCmd(WebSocketSessionRef sessionRef, AttributesSubscriptionCmd cmd) {
String sessionId = sessionRef.getSessionId(); String sessionId = sessionRef.getSessionId();
log.debug("[{}] Processing: {}", sessionId, cmd); log.debug("[{}] Processing: {}", sessionId, cmd);
@ -441,7 +498,7 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi
} }
} }
private void handleWsAttributesSubscriptionByKeys(TelemetryWebSocketSessionRef sessionRef, private void handleWsAttributesSubscriptionByKeys(WebSocketSessionRef sessionRef,
AttributesSubscriptionCmd cmd, String sessionId, EntityId entityId, AttributesSubscriptionCmd cmd, String sessionId, EntityId entityId,
List<String> keys) { List<String> keys) {
FutureCallback<List<AttributeKvEntry>> callback = new FutureCallback<>() { FutureCallback<List<AttributeKvEntry>> callback = new FutureCallback<>() {
@ -465,7 +522,7 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi
.allKeys(false) .allKeys(false)
.keyStates(subState) .keyStates(subState)
.scope(scope) .scope(scope)
.updateConsumer(DefaultTelemetryWebSocketService.this::sendWsMsg) .updateProcessor((subscription, update) -> sendWsMsg(subscription.getSessionId(), update))
.build(); .build();
oldSubService.addSubscription(sub); oldSubService.addSubscription(sub);
} }
@ -492,7 +549,7 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi
} }
} }
private void handleWsHistoryCmd(TelemetryWebSocketSessionRef sessionRef, GetHistoryCmd cmd) { private void handleWsHistoryCmd(WebSocketSessionRef sessionRef, GetHistoryCmd cmd) {
String sessionId = sessionRef.getSessionId(); String sessionId = sessionRef.getSessionId();
WsSessionMetaData sessionMD = wsSessionsMap.get(sessionId); WsSessionMetaData sessionMD = wsSessionsMap.get(sessionId);
if (sessionMD == null) { if (sessionMD == null) {
@ -542,7 +599,7 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi
on(r -> Futures.addCallback(tsService.findAll(sessionRef.getSecurityCtx().getTenantId(), entityId, queries), callback, executor), callback::onFailure)); on(r -> Futures.addCallback(tsService.findAll(sessionRef.getSecurityCtx().getTenantId(), entityId, queries), callback, executor), callback::onFailure));
} }
private void handleWsAttributesSubscription(TelemetryWebSocketSessionRef sessionRef, private void handleWsAttributesSubscription(WebSocketSessionRef sessionRef,
AttributesSubscriptionCmd cmd, String sessionId, EntityId entityId) { AttributesSubscriptionCmd cmd, String sessionId, EntityId entityId) {
FutureCallback<List<AttributeKvEntry>> callback = new FutureCallback<>() { FutureCallback<List<AttributeKvEntry>> callback = new FutureCallback<>() {
@Override @Override
@ -563,7 +620,7 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi
.entityId(entityId) .entityId(entityId)
.allKeys(true) .allKeys(true)
.keyStates(subState) .keyStates(subState)
.updateConsumer(DefaultTelemetryWebSocketService.this::sendWsMsg) .updateProcessor((subscription, update) -> sendWsMsg(subscription.getSessionId(), update))
.scope(scope).build(); .scope(scope).build();
oldSubService.addSubscription(sub); oldSubService.addSubscription(sub);
} }
@ -585,7 +642,7 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi
} }
} }
private void handleWsTimeseriesSubscriptionCmd(TelemetryWebSocketSessionRef sessionRef, TimeseriesSubscriptionCmd cmd) { private void handleWsTimeseriesSubscriptionCmd(WebSocketSessionRef sessionRef, TimeseriesSubscriptionCmd cmd) {
String sessionId = sessionRef.getSessionId(); String sessionId = sessionRef.getSessionId();
log.debug("[{}] Processing: {}", sessionId, cmd); log.debug("[{}] Processing: {}", sessionId, cmd);
@ -605,7 +662,7 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi
} }
} }
private void handleWsTimeseriesSubscriptionByKeys(TelemetryWebSocketSessionRef sessionRef, private void handleWsTimeseriesSubscriptionByKeys(WebSocketSessionRef sessionRef,
TimeseriesSubscriptionCmd cmd, String sessionId, EntityId entityId) { TimeseriesSubscriptionCmd cmd, String sessionId, EntityId entityId) {
long startTs; long startTs;
if (cmd.getTimeWindow() > 0) { if (cmd.getTimeWindow() > 0) {
@ -629,7 +686,7 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi
} }
} }
private void handleWsTimeseriesSubscription(TelemetryWebSocketSessionRef sessionRef, private void handleWsTimeseriesSubscription(WebSocketSessionRef sessionRef,
TimeseriesSubscriptionCmd cmd, String sessionId, EntityId entityId) { TimeseriesSubscriptionCmd cmd, String sessionId, EntityId entityId) {
FutureCallback<List<TsKvEntry>> callback = new FutureCallback<List<TsKvEntry>>() { FutureCallback<List<TsKvEntry>> callback = new FutureCallback<List<TsKvEntry>>() {
@Override @Override
@ -644,7 +701,7 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi
.subscriptionId(cmd.getCmdId()) .subscriptionId(cmd.getCmdId())
.tenantId(sessionRef.getSecurityCtx().getTenantId()) .tenantId(sessionRef.getSecurityCtx().getTenantId())
.entityId(entityId) .entityId(entityId)
.updateConsumer(DefaultTelemetryWebSocketService.this::sendWsMsg) .updateProcessor((subscription, update) -> sendWsMsg(subscription.getSessionId(), update))
.allKeys(true) .allKeys(true)
.keyStates(subState).build(); .keyStates(subState).build();
oldSubService.addSubscription(sub); oldSubService.addSubscription(sub);
@ -667,7 +724,7 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi
on(r -> Futures.addCallback(tsService.findAllLatest(sessionRef.getSecurityCtx().getTenantId(), entityId), callback, executor), callback::onFailure)); on(r -> Futures.addCallback(tsService.findAllLatest(sessionRef.getSecurityCtx().getTenantId(), entityId), callback, executor), callback::onFailure));
} }
private FutureCallback<List<TsKvEntry>> getSubscriptionCallback(final TelemetryWebSocketSessionRef sessionRef, final TimeseriesSubscriptionCmd cmd, final String sessionId, final EntityId entityId, final long startTs, final List<String> keys) { private FutureCallback<List<TsKvEntry>> getSubscriptionCallback(final WebSocketSessionRef sessionRef, final TimeseriesSubscriptionCmd cmd, final String sessionId, final EntityId entityId, final long startTs, final List<String> keys) {
return new FutureCallback<>() { return new FutureCallback<>() {
@Override @Override
public void onSuccess(List<TsKvEntry> data) { public void onSuccess(List<TsKvEntry> data) {
@ -682,7 +739,7 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi
.subscriptionId(cmd.getCmdId()) .subscriptionId(cmd.getCmdId())
.tenantId(sessionRef.getSecurityCtx().getTenantId()) .tenantId(sessionRef.getSecurityCtx().getTenantId())
.entityId(entityId) .entityId(entityId)
.updateConsumer(DefaultTelemetryWebSocketService.this::sendWsMsg) .updateProcessor((subscription, update) -> sendWsMsg(subscription.getSessionId(), update))
.allKeys(false) .allKeys(false)
.keyStates(subState).build(); .keyStates(subState).build();
oldSubService.addSubscription(sub); oldSubService.addSubscription(sub);
@ -702,7 +759,7 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi
}; };
} }
private void unsubscribe(TelemetryWebSocketSessionRef sessionRef, SubscriptionCmd cmd, String sessionId) { private void unsubscribe(WebSocketSessionRef sessionRef, SubscriptionCmd cmd, String sessionId) {
if (cmd.getEntityId() == null || cmd.getEntityId().isEmpty()) { if (cmd.getEntityId() == null || cmd.getEntityId().isEmpty()) {
oldSubService.cancelAllSessionSubscriptions(sessionId); oldSubService.cancelAllSessionSubscriptions(sessionId);
} else { } else {
@ -710,7 +767,7 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi
} }
} }
private boolean validateSubscriptionCmd(TelemetryWebSocketSessionRef sessionRef, EntityDataCmd cmd) { private boolean validateSubscriptionCmd(WebSocketSessionRef sessionRef, EntityDataCmd cmd) {
if (cmd.getCmdId() < 0) { if (cmd.getCmdId() < 0) {
TelemetrySubscriptionUpdate update = new TelemetrySubscriptionUpdate(cmd.getCmdId(), SubscriptionErrorCode.BAD_REQUEST, TelemetrySubscriptionUpdate update = new TelemetrySubscriptionUpdate(cmd.getCmdId(), SubscriptionErrorCode.BAD_REQUEST,
"Cmd id is negative value!"); "Cmd id is negative value!");
@ -725,7 +782,7 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi
return true; return true;
} }
private boolean validateSubscriptionCmd(TelemetryWebSocketSessionRef sessionRef, EntityCountCmd cmd) { private boolean validateSubscriptionCmd(WebSocketSessionRef sessionRef, EntityCountCmd cmd) {
if (cmd.getCmdId() < 0) { if (cmd.getCmdId() < 0) {
TelemetrySubscriptionUpdate update = new TelemetrySubscriptionUpdate(cmd.getCmdId(), SubscriptionErrorCode.BAD_REQUEST, TelemetrySubscriptionUpdate update = new TelemetrySubscriptionUpdate(cmd.getCmdId(), SubscriptionErrorCode.BAD_REQUEST,
"Cmd id is negative value!"); "Cmd id is negative value!");
@ -739,7 +796,7 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi
return true; return true;
} }
private boolean validateSubscriptionCmd(TelemetryWebSocketSessionRef sessionRef, AlarmDataCmd cmd) { private boolean validateSubscriptionCmd(WebSocketSessionRef sessionRef, AlarmDataCmd cmd) {
if (cmd.getCmdId() < 0) { if (cmd.getCmdId() < 0) {
TelemetrySubscriptionUpdate update = new TelemetrySubscriptionUpdate(cmd.getCmdId(), SubscriptionErrorCode.BAD_REQUEST, TelemetrySubscriptionUpdate update = new TelemetrySubscriptionUpdate(cmd.getCmdId(), SubscriptionErrorCode.BAD_REQUEST,
"Cmd id is negative value!"); "Cmd id is negative value!");
@ -754,7 +811,7 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi
return true; return true;
} }
private boolean validateSubscriptionCmd(TelemetryWebSocketSessionRef sessionRef, SubscriptionCmd cmd) { private boolean validateSubscriptionCmd(WebSocketSessionRef sessionRef, SubscriptionCmd cmd) {
if (cmd.getEntityId() == null || cmd.getEntityId().isEmpty()) { if (cmd.getEntityId() == null || cmd.getEntityId().isEmpty()) {
TelemetrySubscriptionUpdate update = new TelemetrySubscriptionUpdate(cmd.getCmdId(), SubscriptionErrorCode.BAD_REQUEST, TelemetrySubscriptionUpdate update = new TelemetrySubscriptionUpdate(cmd.getCmdId(), SubscriptionErrorCode.BAD_REQUEST,
"Device id is empty!"); "Device id is empty!");
@ -764,11 +821,11 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi
return true; return true;
} }
private boolean validateSessionMetadata(TelemetryWebSocketSessionRef sessionRef, SubscriptionCmd cmd, String sessionId) { private boolean validateSessionMetadata(WebSocketSessionRef sessionRef, SubscriptionCmd cmd, String sessionId) {
return validateSessionMetadata(sessionRef, cmd.getCmdId(), sessionId); return validateSessionMetadata(sessionRef, cmd.getCmdId(), sessionId);
} }
private boolean validateSessionMetadata(TelemetryWebSocketSessionRef sessionRef, int cmdId, String sessionId) { private boolean validateSessionMetadata(WebSocketSessionRef sessionRef, int cmdId, String sessionId) {
WsSessionMetaData sessionMD = wsSessionsMap.get(sessionId); WsSessionMetaData sessionMD = wsSessionsMap.get(sessionId);
if (sessionMD == null) { if (sessionMD == null) {
log.warn("[{}] Session meta data not found. ", sessionId); log.warn("[{}] Session meta data not found. ", sessionId);
@ -781,15 +838,15 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi
} }
} }
private void sendWsMsg(TelemetryWebSocketSessionRef sessionRef, EntityDataUpdate update) { private void sendWsMsg(WebSocketSessionRef sessionRef, EntityDataUpdate update) {
sendWsMsg(sessionRef, update.getCmdId(), update); sendWsMsg(sessionRef, update.getCmdId(), update);
} }
private void sendWsMsg(TelemetryWebSocketSessionRef sessionRef, TelemetrySubscriptionUpdate update) { private void sendWsMsg(WebSocketSessionRef sessionRef, TelemetrySubscriptionUpdate update) {
sendWsMsg(sessionRef, update.getSubscriptionId(), update); sendWsMsg(sessionRef, update.getSubscriptionId(), update);
} }
private void sendWsMsg(TelemetryWebSocketSessionRef sessionRef, int cmdId, Object update) { private void sendWsMsg(WebSocketSessionRef sessionRef, int cmdId, Object update) {
try { try {
String msg = jsonMapper.writeValueAsString(update); String msg = jsonMapper.writeValueAsString(update);
executor.submit(() -> { executor.submit(() -> {

2
application/src/main/java/org/thingsboard/server/service/telemetry/InternalTelemetryService.java

@ -41,6 +41,4 @@ public interface InternalTelemetryService extends RuleEngineTelemetryService {
void deleteLatestInternal(TenantId tenantId, EntityId entityId, List<String> keys, FutureCallback<Void> callback); void deleteLatestInternal(TenantId tenantId, EntityId entityId, List<String> keys, FutureCallback<Void> callback);
} }

2
application/src/main/java/org/thingsboard/server/service/telemetry/SessionEvent.java → application/src/main/java/org/thingsboard/server/service/ws/SessionEvent.java

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.thingsboard.server.service.telemetry; package org.thingsboard.server.service.ws;
import lombok.Getter; import lombok.Getter;
import lombok.ToString; import lombok.ToString;

11
application/src/main/java/org/thingsboard/server/service/telemetry/TelemetryWebSocketMsgEndpoint.java → application/src/main/java/org/thingsboard/server/service/ws/WebSocketMsgEndpoint.java

@ -13,20 +13,21 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.thingsboard.server.service.telemetry; package org.thingsboard.server.service.ws;
import org.springframework.web.socket.CloseStatus; import org.springframework.web.socket.CloseStatus;
import org.thingsboard.server.service.ws.WebSocketSessionRef;
import java.io.IOException; import java.io.IOException;
/** /**
* Created by ashvayka on 27.03.18. * Created by ashvayka on 27.03.18.
*/ */
public interface TelemetryWebSocketMsgEndpoint { public interface WebSocketMsgEndpoint {
void send(TelemetryWebSocketSessionRef sessionRef, int subscriptionId, String msg) throws IOException; void send(WebSocketSessionRef sessionRef, int subscriptionId, String msg) throws IOException;
void sendPing(TelemetryWebSocketSessionRef sessionRef, long currentTime) throws IOException; void sendPing(WebSocketSessionRef sessionRef, long currentTime) throws IOException;
void close(TelemetryWebSocketSessionRef sessionRef, CloseStatus withReason) throws IOException; void close(WebSocketSessionRef sessionRef, CloseStatus withReason) throws IOException;
} }

30
application/src/main/java/org/thingsboard/server/service/telemetry/TelemetryWebSocketSessionRef.java → application/src/main/java/org/thingsboard/server/service/ws/WebSocketSessionRef.java

@ -13,9 +13,11 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.thingsboard.server.service.telemetry; package org.thingsboard.server.service.ws;
import lombok.Builder;
import lombok.Getter; import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.model.SecurityUser;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
@ -25,34 +27,25 @@ import java.util.concurrent.atomic.AtomicInteger;
/** /**
* Created by ashvayka on 27.03.18. * Created by ashvayka on 27.03.18.
*/ */
public class TelemetryWebSocketSessionRef { @RequiredArgsConstructor
@Builder
@Getter
public class WebSocketSessionRef {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@Getter
private final String sessionId; private final String sessionId;
@Getter
private final SecurityUser securityCtx; private final SecurityUser securityCtx;
@Getter
private final InetSocketAddress localAddress; private final InetSocketAddress localAddress;
@Getter
private final InetSocketAddress remoteAddress; private final InetSocketAddress remoteAddress;
@Getter private final WebSocketSessionType sessionType;
private final AtomicInteger sessionSubIdSeq; private final AtomicInteger sessionSubIdSeq = new AtomicInteger();
public TelemetryWebSocketSessionRef(String sessionId, SecurityUser securityCtx, InetSocketAddress localAddress, InetSocketAddress remoteAddress) {
this.sessionId = sessionId;
this.securityCtx = securityCtx;
this.localAddress = localAddress;
this.remoteAddress = remoteAddress;
this.sessionSubIdSeq = new AtomicInteger();
}
@Override @Override
public boolean equals(Object o) { public boolean equals(Object o) {
if (this == o) return true; if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false; if (o == null || getClass() != o.getClass()) return false;
TelemetryWebSocketSessionRef that = (TelemetryWebSocketSessionRef) o; WebSocketSessionRef that = (WebSocketSessionRef) o;
return Objects.equals(sessionId, that.sessionId); return Objects.equals(sessionId, that.sessionId);
} }
@ -63,10 +56,11 @@ public class TelemetryWebSocketSessionRef {
@Override @Override
public String toString() { public String toString() {
return "TelemetryWebSocketSessionRef{" + return "WebSocketSessionRef{" +
"sessionId='" + sessionId + '\'' + "sessionId='" + sessionId + '\'' +
", localAddress=" + localAddress + ", localAddress=" + localAddress +
", remoteAddress=" + remoteAddress + ", remoteAddress=" + remoteAddress +
", sessionType=" + sessionType +
'}'; '}';
} }
} }

38
application/src/main/java/org/thingsboard/server/service/ws/WebSocketSessionType.java

@ -0,0 +1,38 @@
/**
* Copyright © 2016-2022 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.ws;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import java.util.Arrays;
import java.util.Optional;
@RequiredArgsConstructor
@Getter
public enum WebSocketSessionType {
TELEMETRY("telemetry"),
NOTIFICATIONS("notifications");
private final String name;
public static Optional<WebSocketSessionType> forName(String name) {
return Arrays.stream(values())
.filter(sessionType -> sessionType.getName().equals(name))
.findFirst();
}
}

10
application/src/main/java/org/thingsboard/server/service/telemetry/WsSessionMetaData.java → application/src/main/java/org/thingsboard/server/service/ws/WsSessionMetaData.java

@ -13,27 +13,27 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.thingsboard.server.service.telemetry; package org.thingsboard.server.service.ws;
/** /**
* Created by ashvayka on 27.03.18. * Created by ashvayka on 27.03.18.
*/ */
public class WsSessionMetaData { public class WsSessionMetaData {
private TelemetryWebSocketSessionRef sessionRef; private WebSocketSessionRef sessionRef;
private long lastActivityTime; private long lastActivityTime;
public WsSessionMetaData(TelemetryWebSocketSessionRef sessionRef) { public WsSessionMetaData(WebSocketSessionRef sessionRef) {
super(); super();
this.sessionRef = sessionRef; this.sessionRef = sessionRef;
this.lastActivityTime = System.currentTimeMillis(); this.lastActivityTime = System.currentTimeMillis();
} }
public TelemetryWebSocketSessionRef getSessionRef() { public WebSocketSessionRef getSessionRef() {
return sessionRef; return sessionRef;
} }
public void setSessionRef(TelemetryWebSocketSessionRef sessionRef) { public void setSessionRef(WebSocketSessionRef sessionRef) {
this.sessionRef = sessionRef; this.sessionRef = sessionRef;
} }

30
application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/MarkNotificationAsReadCmd.java

@ -0,0 +1,30 @@
/**
* Copyright © 2016-2022 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.ws.notification.cmd;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.UUID;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class MarkNotificationAsReadCmd {
private int cmdId;
private UUID notificationId;
}

28
application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/NotificationCmdsWrapper.java

@ -0,0 +1,28 @@
/**
* Copyright © 2016-2022 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.ws.notification.cmd;
import lombok.Data;
import org.thingsboard.server.service.ws.notification.cmd.MarkNotificationAsReadCmd;
import org.thingsboard.server.service.ws.notification.cmd.NotificationsSubCmd;
@Data
public class NotificationCmdsWrapper {
private NotificationsSubCmd unreadSubCmd;
private NotificationsUnsubCmd unreadUnsubCmd;
private MarkNotificationAsReadCmd markAsReadCmd;
}

28
application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/NotificationsSubCmd.java

@ -0,0 +1,28 @@
/**
* Copyright © 2016-2022 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.ws.notification.cmd;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class NotificationsSubCmd {
private int cmdId;
private int limit;
}

24
application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/NotificationsUnsubCmd.java

@ -0,0 +1,24 @@
/**
* Copyright © 2016-2022 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.ws.notification.cmd;
import lombok.Data;
import org.thingsboard.server.service.ws.telemetry.cmd.v2.UnsubscribeCmd;
@Data
public class NotificationsUnsubCmd implements UnsubscribeCmd {
private int cmdId;
}

54
application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/UnreadNotificationsUpdate.java

@ -0,0 +1,54 @@
/**
* Copyright © 2016-2022 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.ws.notification.cmd;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Builder;
import lombok.Getter;
import org.thingsboard.server.common.data.notification.Notification;
import org.thingsboard.server.service.ws.telemetry.cmd.v2.CmdUpdate;
import org.thingsboard.server.service.ws.telemetry.cmd.v2.CmdUpdateType;
import java.util.Collection;
import java.util.List;
@Getter
public class UnreadNotificationsUpdate extends CmdUpdate {
private final Collection<Notification> notifications;
private final Notification update;
private final int totalUnreadCount;
@Builder
@JsonCreator
public UnreadNotificationsUpdate(@JsonProperty("cmdId") int cmdId, @JsonProperty("errorCode") int errorCode,
@JsonProperty("errorMsg") String errorMsg,
@JsonProperty("notifications") Collection<Notification> notifications,
@JsonProperty("update") Notification update,
@JsonProperty("totalUnreadCount") int totalUnreadCount) {
super(cmdId, errorCode, errorMsg);
this.notifications = notifications;
this.update = update;
this.totalUnreadCount = totalUnreadCount;
}
@Override
public CmdUpdateType getCmdUpdateType() {
return CmdUpdateType.NOTIFICATIONS;
}
}

183
application/src/main/java/org/thingsboard/server/service/ws/notification/sub/DefaultNotificationsSubscriptionService.java

@ -0,0 +1,183 @@
/**
* Copyright © 2016-2022 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.ws.notification.sub;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.thingsboard.server.cluster.TbClusterService;
import org.thingsboard.server.common.data.id.IdBased;
import org.thingsboard.server.common.data.id.NotificationId;
import org.thingsboard.server.common.data.id.NotificationRequestId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.notification.Notification;
import org.thingsboard.server.common.data.notification.NotificationStatus;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.msg.queue.ServiceType;
import org.thingsboard.server.common.msg.queue.TbCallback;
import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
import org.thingsboard.server.dao.notification.NotificationService;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.queue.discovery.NotificationsTopicService;
import org.thingsboard.server.queue.discovery.PartitionService;
import org.thingsboard.server.queue.discovery.TbServiceInfoProvider;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.notification.NotificationProcessingService;
import org.thingsboard.server.service.security.model.SecurityUser;
import org.thingsboard.server.service.subscription.TbLocalSubscriptionService;
import org.thingsboard.server.service.subscription.TbSubscriptionUtils;
import org.thingsboard.server.service.telemetry.AbstractSubscriptionService;
import org.thingsboard.server.service.ws.WebSocketSessionRef;
import org.thingsboard.server.service.ws.notification.cmd.MarkNotificationAsReadCmd;
import org.thingsboard.server.service.ws.notification.cmd.NotificationsSubCmd;
import org.thingsboard.server.service.ws.notification.cmd.UnreadNotificationsUpdate;
import org.thingsboard.server.service.ws.telemetry.WebSocketService;
import org.thingsboard.server.service.ws.telemetry.cmd.v2.UnsubscribeCmd;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
@Service
@TbCoreComponent
@Slf4j
public class DefaultNotificationsSubscriptionService extends AbstractSubscriptionService implements NotificationsSubscriptionService {
private final WebSocketService wsService;
private final TbLocalSubscriptionService localSubscriptionService;
private final NotificationService notificationService;
private final TbServiceInfoProvider serviceInfoProvider;
private final NotificationsTopicService notificationsTopicService;
private final NotificationProcessingService notificationProcessingService;
public DefaultNotificationsSubscriptionService(TbClusterService clusterService, PartitionService partitionService,
@Lazy WebSocketService wsService, TbLocalSubscriptionService localSubscriptionService,
NotificationService notificationService, TbServiceInfoProvider serviceInfoProvider,
NotificationsTopicService notificationsTopicService,
@Lazy NotificationProcessingService notificationProcessingService) {
super(clusterService, partitionService);
this.wsService = wsService;
this.localSubscriptionService = localSubscriptionService;
this.notificationService = notificationService;
this.serviceInfoProvider = serviceInfoProvider;
this.notificationsTopicService = notificationsTopicService;
this.notificationProcessingService = notificationProcessingService;
}
@Override
public void handleUnreadNotificationsSubCmd(WebSocketSessionRef sessionRef, NotificationsSubCmd cmd) {
SecurityUser user = sessionRef.getSecurityCtx();
NotificationsSubscription subscription = NotificationsSubscription.builder()
.serviceId(serviceInfoProvider.getServiceId())
.sessionId(sessionRef.getSessionId())
.subscriptionId(cmd.getCmdId())
.tenantId(user.getTenantId())
.entityId(user.getId())
.updateProcessor(this::handleSubscriptionUpdate)
.limit(cmd.getLimit())
.build();
localSubscriptionService.addSubscription(subscription);
fetchUnreadNotifications(subscription);
sendUpdate(sessionRef.getSessionId(), subscription.createFullUpdate());
}
@Override
public void handleMarkAsReadCmd(WebSocketSessionRef sessionRef, MarkNotificationAsReadCmd cmd) {
NotificationId notificationId = new NotificationId(cmd.getNotificationId());
notificationProcessingService.markNotificationAsRead(sessionRef.getSecurityCtx(), notificationId);
}
@Override
public void handleUnsubCmd(WebSocketSessionRef sessionRef, UnsubscribeCmd cmd) {
localSubscriptionService.cancelSubscription(sessionRef.getSessionId(), cmd.getCmdId());
}
private void fetchUnreadNotifications(NotificationsSubscription subscription) {
PageData<Notification> notifications = notificationService.findLatestUnreadNotificationsByUserId(subscription.getTenantId(),
(UserId) subscription.getEntityId(), subscription.getLimit());
subscription.getUnreadNotifications().clear();
subscription.getUnreadNotifications().putAll(notifications.getData().stream().collect(Collectors.toMap(IdBased::getUuidId, n -> n)));
subscription.getTotalUnreadCount().set((int) notifications.getTotalElements());
}
@Override
public void onNewNotification(TenantId tenantId, UserId recipientId, Notification notification) {
onNotificationUpdate(tenantId, recipientId, notification);
}
@Override
public void onNotificationUpdated(TenantId tenantId, UserId recipientId, Notification notification) {
onNotificationUpdate(tenantId, recipientId, notification);
}
private void onNotificationUpdate(TenantId tenantId, UserId recipientId, Notification notification) {
forwardToSubscriptionManagerServiceOrSendToCore(tenantId, recipientId, subscriptionManagerService -> {
subscriptionManagerService.onNotificationUpdate(tenantId, recipientId, notification, TbCallback.EMPTY);
}, () -> {
return TbSubscriptionUtils.notificationUpdateToProto(tenantId, recipientId, notification);
});
}
@Override
public void onNotificationRequestDeleted(TenantId tenantId, NotificationRequestId notificationRequestId) {
TransportProtos.ToCoreMsg notificationRequestDeletedProto = TbSubscriptionUtils.notificationRequestDeletedToProto(tenantId, notificationRequestId);
Set<String> coreServices = new HashSet<>(partitionService.getAllServiceIds(ServiceType.TB_CORE));
for (String serviceId : coreServices) {
TopicPartitionInfo tpi = notificationsTopicService.getNotificationsTopic(ServiceType.TB_CORE, serviceId);
clusterService.pushMsgToCore(tpi, UUID.randomUUID(), notificationRequestDeletedProto, null);
}
}
private void handleSubscriptionUpdate(NotificationsSubscription subscription, NotificationsSubscriptionUpdate subscriptionUpdate) {
if (subscriptionUpdate.getNotification() != null) {
Notification notification = subscriptionUpdate.getNotification();
if (notification.getStatus() == NotificationStatus.READ) {
fetchUnreadNotifications(subscription);
sendUpdate(subscription.getSessionId(), subscription.createFullUpdate());
} else {
Notification previous = subscription.getUnreadNotifications().put(notification.getUuidId(), notification);
if (previous == null) {
subscription.getTotalUnreadCount().incrementAndGet();
Set<UUID> beyondLimit = subscription.getUnreadNotifications().keySet().stream()
.skip(subscription.getLimit())
.collect(Collectors.toSet());
beyondLimit.forEach(notificationId -> subscription.getUnreadNotifications().remove(notificationId));
}
sendUpdate(subscription.getSessionId(), subscription.createPartialUpdate(notification));
}
} else if (subscriptionUpdate.isNotificationRequestDeleted()) {
if (subscription.getUnreadNotifications().values().stream()
.anyMatch(notification -> notification.getRequestId().equals(subscriptionUpdate.getNotificationRequestId()))) {
fetchUnreadNotifications(subscription);
sendUpdate(subscription.getSessionId(), subscription.createFullUpdate());
}
}
}
private void sendUpdate(String sessionId, UnreadNotificationsUpdate update) {
wsService.sendWsMsg(sessionId, update);;
}
@Override
protected String getExecutorPrefix() {
return "notification";
}
}

64
application/src/main/java/org/thingsboard/server/service/ws/notification/sub/NotificationsSubscription.java

@ -0,0 +1,64 @@
/**
* Copyright © 2016-2022 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.ws.notification.sub;
import lombok.Builder;
import lombok.Getter;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.notification.Notification;
import org.thingsboard.server.service.subscription.TbSubscription;
import org.thingsboard.server.service.subscription.TbSubscriptionType;
import org.thingsboard.server.service.ws.notification.cmd.UnreadNotificationsUpdate;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BiConsumer;
@Getter
public class NotificationsSubscription extends TbSubscription<NotificationsSubscriptionUpdate> {
private final Map<UUID, Notification> unreadNotifications = new LinkedHashMap<>();
private final int limit;
private final AtomicInteger totalUnreadCount = new AtomicInteger();
@Builder
public NotificationsSubscription(String serviceId, String sessionId, int subscriptionId, TenantId tenantId, EntityId entityId,
BiConsumer<NotificationsSubscription, NotificationsSubscriptionUpdate> updateProcessor,
int limit) {
super(serviceId, sessionId, subscriptionId, tenantId, entityId, TbSubscriptionType.NOTIFICATIONS, updateProcessor);
this.limit = limit;
}
public UnreadNotificationsUpdate createFullUpdate() {
return UnreadNotificationsUpdate.builder()
.cmdId(getSubscriptionId())
.notifications(unreadNotifications.values())
.totalUnreadCount(totalUnreadCount.get())
.build();
}
public UnreadNotificationsUpdate createPartialUpdate(Notification notification) {
return UnreadNotificationsUpdate.builder()
.cmdId(getSubscriptionId())
.update(notification)
.totalUnreadCount(totalUnreadCount.get())
.build();
}
}

42
application/src/main/java/org/thingsboard/server/service/ws/notification/sub/NotificationsSubscriptionService.java

@ -0,0 +1,42 @@
/**
* Copyright © 2016-2022 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.ws.notification.sub;
import org.thingsboard.server.common.data.id.NotificationRequestId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.notification.Notification;
import org.thingsboard.server.service.ws.WebSocketSessionRef;
import org.thingsboard.server.service.ws.notification.cmd.MarkNotificationAsReadCmd;
import org.thingsboard.server.service.ws.notification.cmd.NotificationsSubCmd;
import org.thingsboard.server.service.ws.telemetry.cmd.v2.UnsubscribeCmd;
public interface NotificationsSubscriptionService {
void handleUnreadNotificationsSubCmd(WebSocketSessionRef sessionRef, NotificationsSubCmd cmd);
void handleMarkAsReadCmd(WebSocketSessionRef sessionRef, MarkNotificationAsReadCmd cmd);
void handleUnsubCmd(WebSocketSessionRef sessionRef, UnsubscribeCmd cmd);
void onNewNotification(TenantId tenantId, UserId recipientId, Notification notification);
void onNotificationUpdated(TenantId tenantId, UserId recipientId, Notification notification);
void onNotificationRequestDeleted(TenantId tenantId, NotificationRequestId notificationRequestId);
}

32
application/src/main/java/org/thingsboard/server/service/ws/notification/sub/NotificationsSubscriptionUpdate.java

@ -0,0 +1,32 @@
/**
* Copyright © 2016-2022 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.ws.notification.sub;
import lombok.Builder;
import lombok.Data;
import org.thingsboard.server.common.data.id.NotificationRequestId;
import org.thingsboard.server.common.data.notification.Notification;
@Data
@Builder
public class NotificationsSubscriptionUpdate {
private final Notification notification;
private final boolean notificationRequestDeleted;
private final NotificationRequestId notificationRequestId;
}

2
application/src/main/java/org/thingsboard/server/service/telemetry/TelemetryFeature.java → application/src/main/java/org/thingsboard/server/service/ws/telemetry/TelemetryFeature.java

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.thingsboard.server.service.telemetry; package org.thingsboard.server.service.ws.telemetry;
/** /**
* Created by ashvayka on 08.05.17. * Created by ashvayka on 08.05.17.

5
application/src/main/java/org/thingsboard/server/service/telemetry/TelemetryWebSocketTextMsg.java → application/src/main/java/org/thingsboard/server/service/ws/telemetry/TelemetryWebSocketTextMsg.java

@ -13,9 +13,10 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.thingsboard.server.service.telemetry; package org.thingsboard.server.service.ws.telemetry;
import lombok.Data; import lombok.Data;
import org.thingsboard.server.service.ws.WebSocketSessionRef;
/** /**
* Created by ashvayka on 27.03.18. * Created by ashvayka on 27.03.18.
@ -23,7 +24,7 @@ import lombok.Data;
@Data @Data
public class TelemetryWebSocketTextMsg { public class TelemetryWebSocketTextMsg {
private final TelemetryWebSocketSessionRef sessionRef; private final WebSocketSessionRef sessionRef;
private final String payload; private final String payload;
} }

15
application/src/main/java/org/thingsboard/server/service/telemetry/TelemetryWebSocketService.java → application/src/main/java/org/thingsboard/server/service/ws/telemetry/WebSocketService.java

@ -13,21 +13,22 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.thingsboard.server.service.telemetry; package org.thingsboard.server.service.ws.telemetry;
import org.springframework.web.socket.CloseStatus; import org.springframework.web.socket.CloseStatus;
import org.thingsboard.server.service.telemetry.cmd.v2.CmdUpdate; import org.thingsboard.server.service.ws.telemetry.cmd.v2.CmdUpdate;
import org.thingsboard.server.service.telemetry.cmd.v2.DataUpdate; import org.thingsboard.server.service.ws.telemetry.sub.TelemetrySubscriptionUpdate;
import org.thingsboard.server.service.telemetry.sub.TelemetrySubscriptionUpdate; import org.thingsboard.server.service.ws.SessionEvent;
import org.thingsboard.server.service.ws.WebSocketSessionRef;
/** /**
* Created by ashvayka on 27.03.18. * Created by ashvayka on 27.03.18.
*/ */
public interface TelemetryWebSocketService { public interface WebSocketService {
void handleWebSocketSessionEvent(TelemetryWebSocketSessionRef sessionRef, SessionEvent sessionEvent); void handleWebSocketSessionEvent(WebSocketSessionRef sessionRef, SessionEvent sessionEvent);
void handleWebSocketMsg(TelemetryWebSocketSessionRef sessionRef, String msg); void handleWebSocketMsg(WebSocketSessionRef sessionRef, String msg);
void sendWsMsg(String sessionId, TelemetrySubscriptionUpdate update); void sendWsMsg(String sessionId, TelemetrySubscriptionUpdate update);

20
application/src/main/java/org/thingsboard/server/service/telemetry/cmd/TelemetryPluginCmdsWrapper.java → application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/TelemetryPluginCmdsWrapper.java

@ -13,18 +13,18 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.thingsboard.server.service.telemetry.cmd; package org.thingsboard.server.service.ws.telemetry.cmd;
import lombok.Data; import lombok.Data;
import org.thingsboard.server.service.telemetry.cmd.v1.AttributesSubscriptionCmd; import org.thingsboard.server.service.ws.telemetry.cmd.v1.AttributesSubscriptionCmd;
import org.thingsboard.server.service.telemetry.cmd.v1.GetHistoryCmd; import org.thingsboard.server.service.ws.telemetry.cmd.v1.GetHistoryCmd;
import org.thingsboard.server.service.telemetry.cmd.v1.TimeseriesSubscriptionCmd; import org.thingsboard.server.service.ws.telemetry.cmd.v1.TimeseriesSubscriptionCmd;
import org.thingsboard.server.service.telemetry.cmd.v2.AlarmDataCmd; import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityCountUnsubscribeCmd;
import org.thingsboard.server.service.telemetry.cmd.v2.AlarmDataUnsubscribeCmd; import org.thingsboard.server.service.ws.telemetry.cmd.v2.AlarmDataCmd;
import org.thingsboard.server.service.telemetry.cmd.v2.EntityCountCmd; import org.thingsboard.server.service.ws.telemetry.cmd.v2.AlarmDataUnsubscribeCmd;
import org.thingsboard.server.service.telemetry.cmd.v2.EntityCountUnsubscribeCmd; import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityCountCmd;
import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataCmd; import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityDataCmd;
import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataUnsubscribeCmd; import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityDataUnsubscribeCmd;
import java.util.List; import java.util.List;

4
application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v1/AttributesSubscriptionCmd.java → application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v1/AttributesSubscriptionCmd.java

@ -13,10 +13,10 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.thingsboard.server.service.telemetry.cmd.v1; package org.thingsboard.server.service.ws.telemetry.cmd.v1;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import org.thingsboard.server.service.telemetry.TelemetryFeature; import org.thingsboard.server.service.ws.telemetry.TelemetryFeature;
/** /**
* @author Andrew Shvayka * @author Andrew Shvayka

2
application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v1/GetHistoryCmd.java → application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v1/GetHistoryCmd.java

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.thingsboard.server.service.telemetry.cmd.v1; package org.thingsboard.server.service.ws.telemetry.cmd.v1;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;

4
application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v1/SubscriptionCmd.java → application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v1/SubscriptionCmd.java

@ -13,12 +13,12 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.thingsboard.server.service.telemetry.cmd.v1; package org.thingsboard.server.service.ws.telemetry.cmd.v1;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import org.thingsboard.server.service.telemetry.TelemetryFeature; import org.thingsboard.server.service.ws.telemetry.TelemetryFeature;
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor

2
application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v1/TelemetryPluginCmd.java → application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v1/TelemetryPluginCmd.java

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.thingsboard.server.service.telemetry.cmd.v1; package org.thingsboard.server.service.ws.telemetry.cmd.v1;
/** /**
* @author Andrew Shvayka * @author Andrew Shvayka

4
application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v1/TimeseriesSubscriptionCmd.java → application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v1/TimeseriesSubscriptionCmd.java

@ -13,12 +13,12 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.thingsboard.server.service.telemetry.cmd.v1; package org.thingsboard.server.service.ws.telemetry.cmd.v1;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import org.thingsboard.server.service.telemetry.TelemetryFeature; import org.thingsboard.server.service.ws.telemetry.TelemetryFeature;
/** /**
* @author Andrew Shvayka * @author Andrew Shvayka

2
application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/AggHistoryCmd.java → application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/AggHistoryCmd.java

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.thingsboard.server.service.telemetry.cmd.v2; package org.thingsboard.server.service.ws.telemetry.cmd.v2;
import lombok.Data; import lombok.Data;

2
application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/AggKey.java → application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/AggKey.java

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.thingsboard.server.service.telemetry.cmd.v2; package org.thingsboard.server.service.ws.telemetry.cmd.v2;
import lombok.Data; import lombok.Data;
import org.thingsboard.server.common.data.kv.Aggregation; import org.thingsboard.server.common.data.kv.Aggregation;

2
application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/AggTimeSeriesCmd.java → application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/AggTimeSeriesCmd.java

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.thingsboard.server.service.telemetry.cmd.v2; package org.thingsboard.server.service.ws.telemetry.cmd.v2;
import lombok.Data; import lombok.Data;

2
application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/AlarmDataCmd.java → application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/AlarmDataCmd.java

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.thingsboard.server.service.telemetry.cmd.v2; package org.thingsboard.server.service.ws.telemetry.cmd.v2;
import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;

2
application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/AlarmDataUnsubscribeCmd.java → application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/AlarmDataUnsubscribeCmd.java

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.thingsboard.server.service.telemetry.cmd.v2; package org.thingsboard.server.service.ws.telemetry.cmd.v2;
import lombok.Data; import lombok.Data;

4
application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/AlarmDataUpdate.java → application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/AlarmDataUpdate.java

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.thingsboard.server.service.telemetry.cmd.v2; package org.thingsboard.server.service.ws.telemetry.cmd.v2;
import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
@ -21,7 +21,7 @@ import lombok.Getter;
import lombok.ToString; import lombok.ToString;
import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.query.AlarmData; import org.thingsboard.server.common.data.query.AlarmData;
import org.thingsboard.server.service.telemetry.sub.SubscriptionErrorCode; import org.thingsboard.server.service.subscription.SubscriptionErrorCode;
import java.util.List; import java.util.List;

2
application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/CmdUpdate.java → application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/CmdUpdate.java

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.thingsboard.server.service.telemetry.cmd.v2; package org.thingsboard.server.service.ws.telemetry.cmd.v2;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;

5
application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/CmdUpdateType.java → application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/CmdUpdateType.java

@ -13,10 +13,11 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.thingsboard.server.service.telemetry.cmd.v2; package org.thingsboard.server.service.ws.telemetry.cmd.v2;
public enum CmdUpdateType { public enum CmdUpdateType {
ENTITY_DATA, ENTITY_DATA,
ALARM_DATA, ALARM_DATA,
COUNT_DATA COUNT_DATA,
NOTIFICATIONS
} }

3
application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/DataCmd.java → application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/DataCmd.java

@ -13,11 +13,10 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.thingsboard.server.service.telemetry.cmd.v2; package org.thingsboard.server.service.ws.telemetry.cmd.v2;
import lombok.Data; import lombok.Data;
import lombok.Getter; import lombok.Getter;
import lombok.NoArgsConstructor;
@Data @Data
public class DataCmd { public class DataCmd {

4
application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/DataUpdate.java → application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/DataUpdate.java

@ -13,11 +13,11 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.thingsboard.server.service.telemetry.cmd.v2; package org.thingsboard.server.service.ws.telemetry.cmd.v2;
import lombok.Getter; import lombok.Getter;
import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.service.telemetry.sub.SubscriptionErrorCode; import org.thingsboard.server.service.subscription.SubscriptionErrorCode;
import java.util.List; import java.util.List;

3
application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/EntityCountCmd.java → application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/EntityCountCmd.java

@ -13,13 +13,12 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.thingsboard.server.service.telemetry.cmd.v2; package org.thingsboard.server.service.ws.telemetry.cmd.v2;
import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Getter; import lombok.Getter;
import org.thingsboard.server.common.data.query.EntityCountQuery; import org.thingsboard.server.common.data.query.EntityCountQuery;
import org.thingsboard.server.common.data.query.EntityDataQuery;
public class EntityCountCmd extends DataCmd { public class EntityCountCmd extends DataCmd {

2
application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/EntityCountUnsubscribeCmd.java → application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/EntityCountUnsubscribeCmd.java

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.thingsboard.server.service.telemetry.cmd.v2; package org.thingsboard.server.service.ws.telemetry.cmd.v2;
import lombok.Data; import lombok.Data;

8
application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/EntityCountUpdate.java → application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/EntityCountUpdate.java

@ -13,17 +13,13 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.thingsboard.server.service.telemetry.cmd.v2; package org.thingsboard.server.service.ws.telemetry.cmd.v2;
import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Getter; import lombok.Getter;
import lombok.ToString; import lombok.ToString;
import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.service.subscription.SubscriptionErrorCode;
import org.thingsboard.server.common.data.query.EntityData;
import org.thingsboard.server.service.telemetry.sub.SubscriptionErrorCode;
import java.util.List;
@ToString @ToString
public class EntityCountUpdate extends CmdUpdate { public class EntityCountUpdate extends CmdUpdate {

2
application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/EntityDataCmd.java → application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/EntityDataCmd.java

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.thingsboard.server.service.telemetry.cmd.v2; package org.thingsboard.server.service.ws.telemetry.cmd.v2;
import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnore;

2
application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/EntityDataUnsubscribeCmd.java → application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/EntityDataUnsubscribeCmd.java

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.thingsboard.server.service.telemetry.cmd.v2; package org.thingsboard.server.service.ws.telemetry.cmd.v2;
import lombok.Data; import lombok.Data;

4
application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/EntityDataUpdate.java → application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/EntityDataUpdate.java

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.thingsboard.server.service.telemetry.cmd.v2; package org.thingsboard.server.service.ws.telemetry.cmd.v2;
import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
@ -21,7 +21,7 @@ import lombok.Getter;
import lombok.ToString; import lombok.ToString;
import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.query.EntityData; import org.thingsboard.server.common.data.query.EntityData;
import org.thingsboard.server.service.telemetry.sub.SubscriptionErrorCode; import org.thingsboard.server.service.subscription.SubscriptionErrorCode;
import java.util.List; import java.util.List;

2
application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/EntityHistoryCmd.java → application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/EntityHistoryCmd.java

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.thingsboard.server.service.telemetry.cmd.v2; package org.thingsboard.server.service.ws.telemetry.cmd.v2;
import lombok.Data; import lombok.Data;
import org.thingsboard.server.common.data.kv.Aggregation; import org.thingsboard.server.common.data.kv.Aggregation;

2
application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/GetTsCmd.java → application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/GetTsCmd.java

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.thingsboard.server.service.telemetry.cmd.v2; package org.thingsboard.server.service.ws.telemetry.cmd.v2;
import org.thingsboard.server.common.data.kv.Aggregation; import org.thingsboard.server.common.data.kv.Aggregation;

2
application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/LatestValueCmd.java → application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/LatestValueCmd.java

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.thingsboard.server.service.telemetry.cmd.v2; package org.thingsboard.server.service.ws.telemetry.cmd.v2;
import lombok.Data; import lombok.Data;
import org.thingsboard.server.common.data.query.EntityKey; import org.thingsboard.server.common.data.query.EntityKey;

2
application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/TimeSeriesCmd.java → application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/TimeSeriesCmd.java

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.thingsboard.server.service.telemetry.cmd.v2; package org.thingsboard.server.service.ws.telemetry.cmd.v2;
import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data; import lombok.Data;

2
application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/UnsubscribeCmd.java → application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/UnsubscribeCmd.java

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.thingsboard.server.service.telemetry.cmd.v2; package org.thingsboard.server.service.ws.telemetry.cmd.v2;
public interface UnsubscribeCmd { public interface UnsubscribeCmd {

12
application/src/main/java/org/thingsboard/server/service/telemetry/sub/AlarmSubscriptionUpdate.java → application/src/main/java/org/thingsboard/server/service/ws/telemetry/sub/AlarmSubscriptionUpdate.java

@ -13,19 +13,11 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.thingsboard.server.service.telemetry.sub; package org.thingsboard.server.service.ws.telemetry.sub;
import lombok.Getter; import lombok.Getter;
import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.Alarm;
import org.thingsboard.server.common.data.kv.TsKvEntry; import org.thingsboard.server.service.subscription.SubscriptionErrorCode;
import org.thingsboard.server.common.data.query.AlarmData;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.stream.Collectors;
public class AlarmSubscriptionUpdate { public class AlarmSubscriptionUpdate {

4
application/src/main/java/org/thingsboard/server/service/telemetry/sub/SubscriptionState.java → application/src/main/java/org/thingsboard/server/service/ws/telemetry/sub/SubscriptionState.java

@ -13,13 +13,13 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.thingsboard.server.service.telemetry.sub; package org.thingsboard.server.service.ws.telemetry.sub;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Getter; import lombok.Getter;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.service.telemetry.TelemetryFeature; import org.thingsboard.server.service.ws.telemetry.TelemetryFeature;
import java.util.Map; import java.util.Map;

3
application/src/main/java/org/thingsboard/server/service/telemetry/sub/TelemetrySubscriptionUpdate.java → application/src/main/java/org/thingsboard/server/service/ws/telemetry/sub/TelemetrySubscriptionUpdate.java

@ -13,9 +13,10 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.thingsboard.server.service.telemetry.sub; package org.thingsboard.server.service.ws.telemetry.sub;
import org.thingsboard.server.common.data.kv.TsKvEntry; import org.thingsboard.server.common.data.kv.TsKvEntry;
import org.thingsboard.server.service.subscription.SubscriptionErrorCode;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;

21
application/src/test/java/org/thingsboard/server/controller/AbstractControllerTest.java

@ -53,6 +53,7 @@ public abstract class AbstractControllerTest extends AbstractNotifyEntityTest {
protected int wsPort; protected int wsPort;
private TbTestWebSocketClient wsClient; // lazy private TbTestWebSocketClient wsClient; // lazy
private TbTestWebSocketClient anotherWsClient; // lazy
public TbTestWebSocketClient getWsClient() { public TbTestWebSocketClient getWsClient() {
if (wsClient == null) { if (wsClient == null) {
@ -69,6 +70,21 @@ public abstract class AbstractControllerTest extends AbstractNotifyEntityTest {
return wsClient; return wsClient;
} }
public TbTestWebSocketClient getAnotherWsClient() {
if (anotherWsClient == null) {
synchronized (this) {
try {
if (anotherWsClient == null) {
anotherWsClient = buildAndConnectWebSocketClient();
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
return anotherWsClient;
}
@Before @Before
public void beforeWsTest() throws Exception { public void beforeWsTest() throws Exception {
// placeholder // placeholder
@ -79,9 +95,12 @@ public abstract class AbstractControllerTest extends AbstractNotifyEntityTest {
if (wsClient != null) { if (wsClient != null) {
wsClient.close(); wsClient.close();
} }
if (anotherWsClient != null) {
anotherWsClient.close();
}
} }
private TbTestWebSocketClient buildAndConnectWebSocketClient() throws URISyntaxException, InterruptedException { protected TbTestWebSocketClient buildAndConnectWebSocketClient() throws URISyntaxException, InterruptedException {
TbTestWebSocketClient wsClient = new TbTestWebSocketClient(new URI(WS_URL + wsPort + "/api/ws/plugins/telemetry?token=" + token)); TbTestWebSocketClient wsClient = new TbTestWebSocketClient(new URI(WS_URL + wsPort + "/api/ws/plugins/telemetry?token=" + token));
assertThat(wsClient.connectBlocking(TIMEOUT, TimeUnit.SECONDS)).isTrue(); assertThat(wsClient.connectBlocking(TIMEOUT, TimeUnit.SECONDS)).isTrue();
return wsClient; return wsClient;

6
application/src/test/java/org/thingsboard/server/controller/BaseWebsocketApiTest.java

@ -44,9 +44,9 @@ import org.thingsboard.server.common.data.query.NumericFilterPredicate;
import org.thingsboard.server.common.data.query.TsValue; import org.thingsboard.server.common.data.query.TsValue;
import org.thingsboard.server.service.subscription.TbAttributeSubscriptionScope; import org.thingsboard.server.service.subscription.TbAttributeSubscriptionScope;
import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService; import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService;
import org.thingsboard.server.service.telemetry.cmd.v2.EntityCountCmd; import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityCountCmd;
import org.thingsboard.server.service.telemetry.cmd.v2.EntityCountUpdate; import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityCountUpdate;
import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataUpdate; import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityDataUpdate;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;

16
application/src/test/java/org/thingsboard/server/controller/TbTestWebSocketClient.java

@ -24,14 +24,14 @@ import org.thingsboard.server.common.data.query.EntityDataPageLink;
import org.thingsboard.server.common.data.query.EntityDataQuery; import org.thingsboard.server.common.data.query.EntityDataQuery;
import org.thingsboard.server.common.data.query.EntityFilter; import org.thingsboard.server.common.data.query.EntityFilter;
import org.thingsboard.server.common.data.query.EntityKey; import org.thingsboard.server.common.data.query.EntityKey;
import org.thingsboard.server.service.telemetry.cmd.TelemetryPluginCmdsWrapper; import org.thingsboard.server.service.ws.telemetry.cmd.TelemetryPluginCmdsWrapper;
import org.thingsboard.server.service.telemetry.cmd.v2.EntityCountCmd; import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityCountCmd;
import org.thingsboard.server.service.telemetry.cmd.v2.EntityCountUpdate; import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityCountUpdate;
import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataCmd; import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityDataCmd;
import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataUpdate; import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityDataUpdate;
import org.thingsboard.server.service.telemetry.cmd.v2.EntityHistoryCmd; import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityHistoryCmd;
import org.thingsboard.server.service.telemetry.cmd.v2.LatestValueCmd; import org.thingsboard.server.service.ws.telemetry.cmd.v2.LatestValueCmd;
import org.thingsboard.server.service.telemetry.cmd.v2.TimeSeriesCmd; import org.thingsboard.server.service.ws.telemetry.cmd.v2.TimeSeriesCmd;
import java.net.URI; import java.net.URI;
import java.nio.channels.NotYetConnectedException; import java.nio.channels.NotYetConnectedException;

75
application/src/test/java/org/thingsboard/server/service/notification/NotificationsWebSocketClient.java

@ -0,0 +1,75 @@
package org.thingsboard.server.service.notification;
import lombok.Getter;
import org.apache.commons.lang3.RandomUtils;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.id.IdBased;
import org.thingsboard.server.common.data.notification.Notification;
import org.thingsboard.server.controller.TbTestWebSocketClient;
import org.thingsboard.server.service.ws.notification.cmd.MarkNotificationAsReadCmd;
import org.thingsboard.server.service.ws.notification.cmd.NotificationCmdsWrapper;
import org.thingsboard.server.service.ws.notification.cmd.NotificationsSubCmd;
import org.thingsboard.server.service.ws.notification.cmd.UnreadNotificationsUpdate;
import org.thingsboard.server.service.ws.notification.sub.NotificationsSubscriptionUpdate;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.UUID;
import java.util.stream.Collectors;
public class NotificationsWebSocketClient extends TbTestWebSocketClient {
private final Map<UUID, Notification> currentNotifications = new LinkedHashMap<>();
@Getter
private int totalUnreadCount;
@Getter
private UnreadNotificationsUpdate lastUpdate;
public NotificationsWebSocketClient(String wsUrl, String token) throws URISyntaxException {
super(new URI(wsUrl + "/api/ws/plugins/notifications?token=" + token));
}
public void subscribeForUnreadNotifications(int limit) {
NotificationCmdsWrapper cmdsWrapper = new NotificationCmdsWrapper();
cmdsWrapper.setUnreadSubCmd(new NotificationsSubCmd(newCmdId(), limit));
sendCmd(cmdsWrapper);
}
public void markNotificationAsRead(UUID notificationId) {
NotificationCmdsWrapper cmdsWrapper = new NotificationCmdsWrapper();
cmdsWrapper.setMarkAsReadCmd(new MarkNotificationAsReadCmd(newCmdId(), notificationId));
sendCmd(cmdsWrapper);
}
private void handleUpdate(UnreadNotificationsUpdate update) {
totalUnreadCount = update.getTotalUnreadCount();
if (update.getNotifications() != null) {
currentNotifications.clear();
currentNotifications.putAll(update.getNotifications().stream().collect(Collectors.toMap(IdBased::getUuidId, n -> n)));
} else if (update.getUpdate() != null) {
Notification notification = update.getUpdate();
currentNotifications.put(notification.getUuidId(), notification);
}
}
public void sendCmd(NotificationCmdsWrapper cmdsWrapper) {
send(JacksonUtil.toString(cmdsWrapper));
}
@Override
public void onMessage(String s) {
UnreadNotificationsUpdate update = JacksonUtil.fromString(s, UnreadNotificationsUpdate.class);
lastUpdate = update;
handleUpdate(update);
super.onMessage(s);
}
private static int newCmdId() {
return RandomUtils.nextInt(1, 1000);
}
}

147
application/src/test/java/org/thingsboard/server/service/notification/NotificationsWsApiTest.java

@ -0,0 +1,147 @@
package org.thingsboard.server.service.notification;
import org.junit.Before;
import org.junit.Test;
import org.thingsboard.server.common.data.id.NotificationTargetId;
import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.notification.Notification;
import org.thingsboard.server.common.data.notification.NotificationRequest;
import org.thingsboard.server.common.data.notification.targets.NotificationTarget;
import org.thingsboard.server.common.data.notification.targets.SingleUserNotificationTargetConfig;
import org.thingsboard.server.controller.AbstractControllerTest;
import org.thingsboard.server.controller.TbTestWebSocketClient;
import org.thingsboard.server.dao.service.DaoSqlTest;
import org.thingsboard.server.service.ws.notification.cmd.UnreadNotificationsUpdate;
import java.net.URISyntaxException;
import java.util.concurrent.TimeUnit;
import static org.assertj.core.api.Assertions.assertThat;
@DaoSqlTest
public class NotificationsWsApiTest extends AbstractControllerTest {
@Before
public void beforeEach() throws Exception {
loginTenantAdmin();
}
@Test
public void testSubscribingToUnreadNotifications_multipleSessions() throws Exception {
NotificationTarget notificationTarget = createNotificationTarget(tenantAdminUserId);
String notificationText1 = "Notification 1";
submitNotificationRequest(notificationTarget.getId(), "Just a test", notificationText1);
String notificationText2 = "Notification 2";
submitNotificationRequest(notificationTarget.getId(), "Just a test", notificationText2);
getWsClient().subscribeForUnreadNotifications(10);
getAnotherWsClient().subscribeForUnreadNotifications(10);
getWsClient().waitForReply();
getAnotherWsClient().waitForReply();
checkFullNotificationsUpdate(getWsClient().getLastUpdate(), notificationText1, notificationText2);
checkFullNotificationsUpdate(getAnotherWsClient().getLastUpdate(), notificationText1, notificationText2);
}
@Test
public void testReceivingNotificationUpdates_multipleSessions() {
getWsClient().subscribeForUnreadNotifications(10);
getAnotherWsClient().subscribeForUnreadNotifications(10);
getWsClient().waitForReply();
getAnotherWsClient().waitForReply();
UnreadNotificationsUpdate notificationsUpdate = getWsClient().getLastUpdate();
assertThat(notificationsUpdate.getTotalUnreadCount()).isZero();
getWsClient().registerWaitForUpdate();
getAnotherWsClient().registerWaitForUpdate();
NotificationTarget notificationTarget = createNotificationTarget(tenantAdminUserId);
String notificationText = "Notification 1";
submitNotificationRequest(notificationTarget.getId(), "Just a test", notificationText);
getWsClient().waitForUpdate();
getAnotherWsClient().waitForUpdate();
checkPartialNotificationsUpdate(getWsClient().getLastUpdate(), notificationText, 1);
checkPartialNotificationsUpdate(getAnotherWsClient().getLastUpdate(), notificationText, 1);
}
@Test
public void testMarkingAsRead_multipleSessions() {
getWsClient().subscribeForUnreadNotifications(10);
getAnotherWsClient().subscribeForUnreadNotifications(10);
getWsClient().waitForReply();
getAnotherWsClient().waitForReply();
NotificationTarget notificationTarget = createNotificationTarget(tenantAdminUserId);
getWsClient().registerWaitForUpdate();
String notificationText1 = "Notification 1";
submitNotificationRequest(notificationTarget.getId(), "Just a test", notificationText1);
getWsClient().waitForUpdate();
Notification notification1 = getWsClient().getLastUpdate().getUpdate();
getWsClient().registerWaitForUpdate();
String notificationText2 = "Notification 2";
submitNotificationRequest(notificationTarget.getId(), "Just a test", notificationText2);
getWsClient().waitForUpdate();
assertThat(getWsClient().getLastUpdate().getTotalUnreadCount()).isEqualTo(2);
getWsClient().registerWaitForUpdate();
getAnotherWsClient().registerWaitForUpdate();
getWsClient().markNotificationAsRead(notification1.getUuidId());
getWsClient().waitForUpdate();
getAnotherWsClient().waitForUpdate();
checkFullNotificationsUpdate(getWsClient().getLastUpdate(), notificationText2);
checkFullNotificationsUpdate(getAnotherWsClient().getLastUpdate(), notificationText2);
}
public void testReceivingUpdatesWhenSubscriptionAtAnotherInstance() {}
private void checkFullNotificationsUpdate(UnreadNotificationsUpdate notificationsUpdate, String... expectedNotifications) {
assertThat(notificationsUpdate.getNotifications()).extracting(Notification::getText).containsOnly(expectedNotifications);
assertThat(notificationsUpdate.getTotalUnreadCount()).isEqualTo(expectedNotifications.length);
}
private void checkPartialNotificationsUpdate(UnreadNotificationsUpdate notificationsUpdate, String expectedNotification, int expectedUnreadCount) {
assertThat(notificationsUpdate.getUpdate()).extracting(Notification::getText).isEqualTo(expectedNotification);
assertThat(notificationsUpdate.getTotalUnreadCount()).isEqualTo(expectedUnreadCount);
}
private NotificationTarget createNotificationTarget(UserId userId) {
NotificationTarget notificationTarget = new NotificationTarget();
notificationTarget.setTenantId(tenantId);
notificationTarget.setName("User " + userId);
SingleUserNotificationTargetConfig config = new SingleUserNotificationTargetConfig();
config.setUserId(userId);
notificationTarget.setConfiguration(config);
return doPost("/api/notification/target", notificationTarget, NotificationTarget.class);
}
private NotificationRequest submitNotificationRequest(NotificationTargetId targetId, String notificationReason, String text) {
NotificationRequest notificationRequest = NotificationRequest.builder()
.tenantId(tenantId)
.targetId(targetId)
.notificationReason(notificationReason)
.textTemplate(text)
.build();
return doPost("/api/notification/request", notificationRequest, NotificationRequest.class);
}
@Override
protected TbTestWebSocketClient buildAndConnectWebSocketClient() throws URISyntaxException, InterruptedException {
NotificationsWebSocketClient wsClient = new NotificationsWebSocketClient(WS_URL + wsPort, token);
assertThat(wsClient.connectBlocking(TIMEOUT, TimeUnit.SECONDS)).isTrue();
return wsClient;
}
@Override
public NotificationsWebSocketClient getWsClient() {
return (NotificationsWebSocketClient) super.getWsClient();
}
@Override
public NotificationsWebSocketClient getAnotherWsClient() {
return (NotificationsWebSocketClient) super.getAnotherWsClient();
}
}

8
application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java

@ -64,10 +64,10 @@ import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.common.data.security.DeviceCredentialsType; import org.thingsboard.server.common.data.security.DeviceCredentialsType;
import org.thingsboard.server.controller.AbstractControllerTest; import org.thingsboard.server.controller.AbstractControllerTest;
import org.thingsboard.server.dao.service.DaoSqlTest; import org.thingsboard.server.dao.service.DaoSqlTest;
import org.thingsboard.server.service.telemetry.cmd.TelemetryPluginCmdsWrapper; import org.thingsboard.server.service.ws.telemetry.cmd.TelemetryPluginCmdsWrapper;
import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataCmd; import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityDataCmd;
import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataUpdate; import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityDataUpdate;
import org.thingsboard.server.service.telemetry.cmd.v2.LatestValueCmd; import org.thingsboard.server.service.ws.telemetry.cmd.v2.LatestValueCmd;
import org.thingsboard.server.transport.lwm2m.client.LwM2MTestClient; import org.thingsboard.server.transport.lwm2m.client.LwM2MTestClient;
import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext;
import org.thingsboard.server.transport.lwm2m.server.uplink.DefaultLwM2mUplinkMsgHandler; import org.thingsboard.server.transport.lwm2m.server.uplink.DefaultLwM2mUplinkMsgHandler;

3
application/src/test/java/org/thingsboard/server/transport/mqtt/attributes/AbstractMqttAttributesIntegrationTest.java

@ -22,7 +22,6 @@ import com.google.protobuf.InvalidProtocolBufferException;
import com.squareup.wire.schema.internal.parser.ProtoFileElement; import com.squareup.wire.schema.internal.parser.ProtoFileElement;
import io.netty.handler.codec.mqtt.MqttQoS; import io.netty.handler.codec.mqtt.MqttQoS;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.test.context.TestPropertySource;
import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.TransportPayloadType; import org.thingsboard.server.common.data.TransportPayloadType;
@ -38,7 +37,7 @@ import org.thingsboard.server.common.data.query.EntityKeyType;
import org.thingsboard.server.common.data.query.SingleEntityFilter; import org.thingsboard.server.common.data.query.SingleEntityFilter;
import org.thingsboard.server.gen.transport.TransportApiProtos; import org.thingsboard.server.gen.transport.TransportApiProtos;
import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataUpdate; import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityDataUpdate;
import org.thingsboard.server.transport.mqtt.AbstractMqttIntegrationTest; import org.thingsboard.server.transport.mqtt.AbstractMqttIntegrationTest;
import org.thingsboard.server.transport.mqtt.MqttTestCallback; import org.thingsboard.server.transport.mqtt.MqttTestCallback;
import org.thingsboard.server.transport.mqtt.MqttTestClient; import org.thingsboard.server.transport.mqtt.MqttTestClient;

30
common/cluster-api/src/main/proto/queue.proto

@ -547,6 +547,11 @@ message TbAlarmSubscriptionProto {
int64 ts = 2; int64 ts = 2;
} }
message NotificationsSubscriptionProto {
TbSubscriptionProto sub = 1;
int32 limit = 2;
}
message TbSubscriptionUpdateProto { message TbSubscriptionUpdateProto {
string sessionId = 1; string sessionId = 1;
int32 subscriptionId = 2; int32 subscriptionId = 2;
@ -564,6 +569,27 @@ message TbAlarmSubscriptionUpdateProto {
bool deleted = 6; bool deleted = 6;
} }
message NotificationsSubscriptionUpdateProto {
string sessionId = 1;
int32 subscriptionId = 2;
string notification = 3;
}
message NotificationUpdateProto {
int64 tenantIdMSB = 1;
int64 tenantIdLSB = 2;
int64 recipientIdMSB = 3;
int64 recipientIdLSB = 4;
string notification = 5;
}
message NotificationRequestDeleteProto {
int64 tenantIdMSB = 1;
int64 tenantIdLSB = 2;
int64 notificationRequestIdMSB = 3;
int64 notificationRequestIdLSB = 4;
}
message TbAttributeUpdateProto { message TbAttributeUpdateProto {
string entityType = 1; string entityType = 1;
int64 entityIdMSB = 2; int64 entityIdMSB = 2;
@ -665,11 +691,15 @@ message SubscriptionMgrMsgProto {
TbAlarmUpdateProto alarmUpdate = 8; TbAlarmUpdateProto alarmUpdate = 8;
TbAlarmDeleteProto alarmDelete = 9; TbAlarmDeleteProto alarmDelete = 9;
TbTimeSeriesDeleteProto tsDelete = 10; TbTimeSeriesDeleteProto tsDelete = 10;
NotificationsSubscriptionProto notificationsSub = 11;
NotificationUpdateProto notificationUpdate = 12;
NotificationRequestDeleteProto notificationRequestDelete = 13;
} }
message LocalSubscriptionServiceMsgProto { message LocalSubscriptionServiceMsgProto {
TbSubscriptionUpdateProto subUpdate = 1; TbSubscriptionUpdateProto subUpdate = 1;
TbAlarmSubscriptionUpdateProto alarmSubUpdate = 2; TbAlarmSubscriptionUpdateProto alarmSubUpdate = 2;
NotificationsSubscriptionUpdateProto notificationsSubUpdate = 3;
} }
message FromDeviceRPCResponseProto { message FromDeviceRPCResponseProto {

9
common/data/src/main/java/org/thingsboard/server/common/data/StringUtils.java

@ -142,6 +142,15 @@ public class StringUtils {
return org.apache.commons.lang3.StringUtils.equals(str1, str2); return org.apache.commons.lang3.StringUtils.equals(str1, str2);
} }
public static boolean equalsAny(String string, String... otherStrings) {
for (String otherString : otherStrings) {
if (equals(string, otherString)) {
return true;
}
}
return false;
}
public static String substringAfterLast(String str, String sep) { public static String substringAfterLast(String str, String sep) {
return org.apache.commons.lang3.StringUtils.substringAfterLast(str, sep); return org.apache.commons.lang3.StringUtils.substringAfterLast(str, sep);
} }

17
dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationService.java

@ -19,7 +19,9 @@ import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.thingsboard.server.common.data.id.NotificationId; import org.thingsboard.server.common.data.id.NotificationId;
import org.thingsboard.server.common.data.id.NotificationRequestId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.notification.Notification; import org.thingsboard.server.common.data.notification.Notification;
@ -55,11 +57,22 @@ public class DefaultNotificationService implements NotificationService {
return notificationRequestDao.save(tenantId, notificationRequest); return notificationRequestDao.save(tenantId, notificationRequest);
} }
@Override
public NotificationRequest findNotificationRequestById(TenantId tenantId, NotificationRequestId id) {
return notificationRequestDao.findById(tenantId, id.getId());
}
@Override @Override
public PageData<NotificationRequest> findNotificationRequestsByTenantIdAndPageLink(TenantId tenantId, PageLink pageLink) { public PageData<NotificationRequest> findNotificationRequestsByTenantIdAndPageLink(TenantId tenantId, PageLink pageLink) {
return notificationRequestDao.findByTenantIdAndPageLink(tenantId, pageLink); return notificationRequestDao.findByTenantIdAndPageLink(tenantId, pageLink);
} }
// ON DELETE CASCADE is used: notifications for request are deleted as well
@Override
public void deleteNotificationRequest(TenantId tenantId, NotificationRequestId id) {
notificationRequestDao.removeById(tenantId, id.getId());
}
@Override @Override
public Notification createNotification(TenantId tenantId, Notification notification) { public Notification createNotification(TenantId tenantId, Notification notification) {
if (notification.getId() != null) { if (notification.getId() != null) {
@ -68,9 +81,11 @@ public class DefaultNotificationService implements NotificationService {
return notificationDao.save(tenantId, notification); return notificationDao.save(tenantId, notification);
} }
@Transactional
@Override @Override
public void updateNotificationStatus(TenantId tenantId, NotificationId notificationId, NotificationStatus status) { public Notification updateNotificationStatus(TenantId tenantId, NotificationId notificationId, NotificationStatus status) {
notificationDao.updateStatus(tenantId, notificationId, status); notificationDao.updateStatus(tenantId, notificationId, status);
return notificationDao.findById(tenantId, notificationId.getId());
} }
@Override @Override

6
dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationTargetService.java

@ -42,6 +42,7 @@ public class DefaultNotificationTargetService implements NotificationTargetServi
@Override @Override
public NotificationTarget saveNotificationTarget(TenantId tenantId, NotificationTarget notificationTarget) { public NotificationTarget saveNotificationTarget(TenantId tenantId, NotificationTarget notificationTarget) {
notificationTarget.setTenantId(tenantId);
validator.validate(notificationTarget, NotificationTarget::getTenantId); validator.validate(notificationTarget, NotificationTarget::getTenantId);
return notificationTargetDao.save(tenantId, notificationTarget); return notificationTargetDao.save(tenantId, notificationTarget);
} }
@ -81,6 +82,11 @@ public class DefaultNotificationTargetService implements NotificationTargetServi
} }
private static class NotificationTargetValidator extends DataValidator<NotificationTarget> { private static class NotificationTargetValidator extends DataValidator<NotificationTarget> {
@Override
protected void validateDataImpl(TenantId tenantId, NotificationTarget notificationTarget) {
super.validateDataImpl(tenantId, notificationTarget);
}
} }
} }

Loading…
Cancel
Save