Commit 856cc3722a9dac2f5551bd5ff9f9df8c6ccec1a6

Authored by Andrii Shvaika
1 parent 969a686c

Forward device messages to rule chain from device profile

@@ -105,6 +105,7 @@ import org.thingsboard.server.queue.discovery.PartitionService; @@ -105,6 +105,7 @@ import org.thingsboard.server.queue.discovery.PartitionService;
105 import org.thingsboard.server.queue.provider.TbQueueProducerProvider; 105 import org.thingsboard.server.queue.provider.TbQueueProducerProvider;
106 import org.thingsboard.server.queue.util.TbCoreComponent; 106 import org.thingsboard.server.queue.util.TbCoreComponent;
107 import org.thingsboard.server.service.component.ComponentDiscoveryService; 107 import org.thingsboard.server.service.component.ComponentDiscoveryService;
  108 +import org.thingsboard.server.service.profile.TbDeviceProfileCache;
108 import org.thingsboard.server.service.queue.TbClusterService; 109 import org.thingsboard.server.service.queue.TbClusterService;
109 import org.thingsboard.server.service.security.model.SecurityUser; 110 import org.thingsboard.server.service.security.model.SecurityUser;
110 import org.thingsboard.server.service.security.permission.AccessControlService; 111 import org.thingsboard.server.service.security.permission.AccessControlService;
@@ -210,6 +211,9 @@ public abstract class BaseController { @@ -210,6 +211,9 @@ public abstract class BaseController {
210 @Autowired 211 @Autowired
211 protected TbQueueProducerProvider producerProvider; 212 protected TbQueueProducerProvider producerProvider;
212 213
  214 + @Autowired
  215 + protected TbDeviceProfileCache deviceProfileCache;
  216 +
213 @Value("${server.log_controller_error_stack_trace}") 217 @Value("${server.log_controller_error_stack_trace}")
214 @Getter 218 @Getter
215 private boolean logControllerErrorStackTrace; 219 private boolean logControllerErrorStackTrace;
@@ -34,6 +34,7 @@ import org.thingsboard.server.common.data.exception.ThingsboardException; @@ -34,6 +34,7 @@ import org.thingsboard.server.common.data.exception.ThingsboardException;
34 import org.thingsboard.server.common.data.id.DeviceProfileId; 34 import org.thingsboard.server.common.data.id.DeviceProfileId;
35 import org.thingsboard.server.common.data.page.PageData; 35 import org.thingsboard.server.common.data.page.PageData;
36 import org.thingsboard.server.common.data.page.PageLink; 36 import org.thingsboard.server.common.data.page.PageLink;
  37 +import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
37 import org.thingsboard.server.queue.util.TbCoreComponent; 38 import org.thingsboard.server.queue.util.TbCoreComponent;
38 import org.thingsboard.server.service.security.permission.Operation; 39 import org.thingsboard.server.service.security.permission.Operation;
39 import org.thingsboard.server.service.security.permission.Resource; 40 import org.thingsboard.server.service.security.permission.Resource;
@@ -86,13 +87,17 @@ public class DeviceProfileController extends BaseController { @@ -86,13 +87,17 @@ public class DeviceProfileController extends BaseController {
86 @ResponseBody 87 @ResponseBody
87 public DeviceProfile saveDeviceProfile(@RequestBody DeviceProfile deviceProfile) throws ThingsboardException { 88 public DeviceProfile saveDeviceProfile(@RequestBody DeviceProfile deviceProfile) throws ThingsboardException {
88 try { 89 try {
  90 + boolean created = deviceProfile.getId() == null;
89 deviceProfile.setTenantId(getTenantId()); 91 deviceProfile.setTenantId(getTenantId());
90 92
91 checkEntity(deviceProfile.getId(), deviceProfile, Resource.DEVICE_PROFILE); 93 checkEntity(deviceProfile.getId(), deviceProfile, Resource.DEVICE_PROFILE);
92 94
93 DeviceProfile savedDeviceProfile = checkNotNull(deviceProfileService.saveDeviceProfile(deviceProfile)); 95 DeviceProfile savedDeviceProfile = checkNotNull(deviceProfileService.saveDeviceProfile(deviceProfile));
94 96
  97 + deviceProfileCache.put(savedDeviceProfile);
95 tbClusterService.onDeviceProfileChange(savedDeviceProfile, null); 98 tbClusterService.onDeviceProfileChange(savedDeviceProfile, null);
  99 + tbClusterService.onEntityStateChange(deviceProfile.getTenantId(), savedDeviceProfile.getId(),
  100 + created ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED);
96 101
97 logEntityAction(savedDeviceProfile.getId(), savedDeviceProfile, 102 logEntityAction(savedDeviceProfile.getId(), savedDeviceProfile,
98 null, 103 null,
@@ -115,6 +120,10 @@ public class DeviceProfileController extends BaseController { @@ -115,6 +120,10 @@ public class DeviceProfileController extends BaseController {
115 DeviceProfileId deviceProfileId = new DeviceProfileId(toUUID(strDeviceProfileId)); 120 DeviceProfileId deviceProfileId = new DeviceProfileId(toUUID(strDeviceProfileId));
116 DeviceProfile deviceProfile = checkDeviceProfileId(deviceProfileId, Operation.DELETE); 121 DeviceProfile deviceProfile = checkDeviceProfileId(deviceProfileId, Operation.DELETE);
117 deviceProfileService.deleteDeviceProfile(getTenantId(), deviceProfileId); 122 deviceProfileService.deleteDeviceProfile(getTenantId(), deviceProfileId);
  123 + deviceProfileCache.evict(deviceProfileId);
  124 +
  125 + tbClusterService.onDeviceProfileDelete(deviceProfile, null);
  126 + tbClusterService.onEntityStateChange(deviceProfile.getTenantId(), deviceProfile.getId(), ComponentLifecycleEvent.DELETED);
118 127
119 logEntityAction(deviceProfileId, deviceProfile, 128 logEntityAction(deviceProfileId, deviceProfile,
120 null, 129 null,
@@ -180,10 +189,10 @@ public class DeviceProfileController extends BaseController { @@ -180,10 +189,10 @@ public class DeviceProfileController extends BaseController {
180 @RequestMapping(value = "/deviceProfileInfos", params = {"pageSize", "page"}, method = RequestMethod.GET) 189 @RequestMapping(value = "/deviceProfileInfos", params = {"pageSize", "page"}, method = RequestMethod.GET)
181 @ResponseBody 190 @ResponseBody
182 public PageData<DeviceProfileInfo> getDeviceProfileInfos(@RequestParam int pageSize, 191 public PageData<DeviceProfileInfo> getDeviceProfileInfos(@RequestParam int pageSize,
183 - @RequestParam int page,  
184 - @RequestParam(required = false) String textSearch,  
185 - @RequestParam(required = false) String sortProperty,  
186 - @RequestParam(required = false) String sortOrder) throws ThingsboardException { 192 + @RequestParam int page,
  193 + @RequestParam(required = false) String textSearch,
  194 + @RequestParam(required = false) String sortProperty,
  195 + @RequestParam(required = false) String sortOrder) throws ThingsboardException {
187 try { 196 try {
188 PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); 197 PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
189 return checkNotNull(deviceProfileService.findDeviceProfileInfos(getTenantId(), pageLink)); 198 return checkNotNull(deviceProfileService.findDeviceProfileInfos(getTenantId(), pageLink));
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.service.profile;
  17 +
  18 +import lombok.extern.slf4j.Slf4j;
  19 +import org.springframework.stereotype.Service;
  20 +import org.thingsboard.server.common.data.Device;
  21 +import org.thingsboard.server.common.data.DeviceProfile;
  22 +import org.thingsboard.server.common.data.id.DeviceId;
  23 +import org.thingsboard.server.common.data.id.DeviceProfileId;
  24 +import org.thingsboard.server.common.data.id.TenantId;
  25 +import org.thingsboard.server.dao.device.DeviceProfileService;
  26 +import org.thingsboard.server.dao.device.DeviceService;
  27 +
  28 +import java.util.concurrent.ConcurrentHashMap;
  29 +import java.util.concurrent.ConcurrentMap;
  30 +import java.util.concurrent.locks.Lock;
  31 +import java.util.concurrent.locks.ReentrantLock;
  32 +
  33 +@Service
  34 +@Slf4j
  35 +public class DefaultTbDeviceProfileCache implements TbDeviceProfileCache {
  36 +
  37 + private final Lock deviceProfileFetchLock = new ReentrantLock();
  38 + private final DeviceProfileService deviceProfileService;
  39 + private final DeviceService deviceService;
  40 +
  41 + private final ConcurrentMap<DeviceProfileId, DeviceProfile> deviceProfilesMap = new ConcurrentHashMap<>();
  42 + private final ConcurrentMap<DeviceId, DeviceProfileId> devicesMap = new ConcurrentHashMap<>();
  43 +
  44 + public DefaultTbDeviceProfileCache(DeviceProfileService deviceProfileService, DeviceService deviceService) {
  45 + this.deviceProfileService = deviceProfileService;
  46 + this.deviceService = deviceService;
  47 + }
  48 +
  49 + @Override
  50 + public DeviceProfile get(TenantId tenantId, DeviceProfileId deviceProfileId) {
  51 + DeviceProfile profile = deviceProfilesMap.get(deviceProfileId);
  52 + if (profile == null) {
  53 + deviceProfileFetchLock.lock();
  54 + profile = deviceProfilesMap.get(deviceProfileId);
  55 + if (profile == null) {
  56 + try {
  57 + profile = deviceProfileService.findDeviceProfileById(tenantId, deviceProfileId);
  58 + if (profile != null) {
  59 + deviceProfilesMap.put(deviceProfileId, profile);
  60 + }
  61 + } finally {
  62 + deviceProfileFetchLock.unlock();
  63 + }
  64 + }
  65 + }
  66 + return profile;
  67 + }
  68 +
  69 + @Override
  70 + public DeviceProfile get(TenantId tenantId, DeviceId deviceId) {
  71 + DeviceProfileId profileId = devicesMap.get(deviceId);
  72 + if (profileId == null) {
  73 + Device device = deviceService.findDeviceById(tenantId, deviceId);
  74 + if (device != null) {
  75 + profileId = device.getDeviceProfileId();
  76 + devicesMap.put(deviceId, profileId);
  77 + }
  78 + }
  79 + return get(tenantId, profileId);
  80 + }
  81 +
  82 + @Override
  83 + public void put(DeviceProfile profile) {
  84 + if (profile.getId() != null) {
  85 + deviceProfilesMap.put(profile.getId(), profile);
  86 + }
  87 + }
  88 +
  89 + @Override
  90 + public void evict(DeviceProfileId profileId) {
  91 + deviceProfilesMap.remove(profileId);
  92 + }
  93 +
  94 + @Override
  95 + public void evict(DeviceId deviceId) {
  96 + devicesMap.remove(deviceId);
  97 + }
  98 +
  99 +}
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.service.profile;
  17 +
  18 +import org.thingsboard.server.common.data.DeviceProfile;
  19 +import org.thingsboard.server.common.data.id.DeviceId;
  20 +import org.thingsboard.server.common.data.id.DeviceProfileId;
  21 +import org.thingsboard.server.common.data.id.TenantId;
  22 +
  23 +public interface TbDeviceProfileCache {
  24 +
  25 + DeviceProfile get(TenantId tenantId, DeviceProfileId deviceProfileId);
  26 +
  27 + DeviceProfile get(TenantId tenantId, DeviceId deviceId);
  28 +
  29 + void put(DeviceProfile profile);
  30 +
  31 + void evict(DeviceProfileId id);
  32 +
  33 + void evict(DeviceId id);
  34 +
  35 +}
@@ -23,13 +23,17 @@ import org.springframework.stereotype.Service; @@ -23,13 +23,17 @@ import org.springframework.stereotype.Service;
23 import org.thingsboard.rule.engine.api.msg.ToDeviceActorNotificationMsg; 23 import org.thingsboard.rule.engine.api.msg.ToDeviceActorNotificationMsg;
24 import org.thingsboard.server.common.data.DeviceProfile; 24 import org.thingsboard.server.common.data.DeviceProfile;
25 import org.thingsboard.server.common.data.EntityType; 25 import org.thingsboard.server.common.data.EntityType;
  26 +import org.thingsboard.server.common.data.id.DeviceId;
  27 +import org.thingsboard.server.common.data.id.DeviceProfileId;
26 import org.thingsboard.server.common.data.id.EntityId; 28 import org.thingsboard.server.common.data.id.EntityId;
  29 +import org.thingsboard.server.common.data.id.RuleChainId;
27 import org.thingsboard.server.common.data.id.TenantId; 30 import org.thingsboard.server.common.data.id.TenantId;
28 import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; 31 import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
29 import org.thingsboard.server.common.msg.TbMsg; 32 import org.thingsboard.server.common.msg.TbMsg;
30 import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg; 33 import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg;
31 import org.thingsboard.server.common.msg.queue.ServiceType; 34 import org.thingsboard.server.common.msg.queue.ServiceType;
32 import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; 35 import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
  36 +import org.thingsboard.server.common.transport.util.DataDecodingEncodingService;
33 import org.thingsboard.server.gen.transport.TransportProtos; 37 import org.thingsboard.server.gen.transport.TransportProtos;
34 import org.thingsboard.server.gen.transport.TransportProtos.FromDeviceRPCResponseProto; 38 import org.thingsboard.server.gen.transport.TransportProtos.FromDeviceRPCResponseProto;
35 import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; 39 import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg;
@@ -42,7 +46,7 @@ import org.thingsboard.server.queue.TbQueueProducer; @@ -42,7 +46,7 @@ import org.thingsboard.server.queue.TbQueueProducer;
42 import org.thingsboard.server.queue.common.TbProtoQueueMsg; 46 import org.thingsboard.server.queue.common.TbProtoQueueMsg;
43 import org.thingsboard.server.queue.discovery.PartitionService; 47 import org.thingsboard.server.queue.discovery.PartitionService;
44 import org.thingsboard.server.queue.provider.TbQueueProducerProvider; 48 import org.thingsboard.server.queue.provider.TbQueueProducerProvider;
45 -import org.thingsboard.server.common.transport.util.DataDecodingEncodingService; 49 +import org.thingsboard.server.service.profile.TbDeviceProfileCache;
46 import org.thingsboard.server.service.rpc.FromDeviceRpcResponse; 50 import org.thingsboard.server.service.rpc.FromDeviceRpcResponse;
47 51
48 import java.util.HashSet; 52 import java.util.HashSet;
@@ -66,11 +70,13 @@ public class DefaultTbClusterService implements TbClusterService { @@ -66,11 +70,13 @@ public class DefaultTbClusterService implements TbClusterService {
66 private final TbQueueProducerProvider producerProvider; 70 private final TbQueueProducerProvider producerProvider;
67 private final PartitionService partitionService; 71 private final PartitionService partitionService;
68 private final DataDecodingEncodingService encodingService; 72 private final DataDecodingEncodingService encodingService;
  73 + private final TbDeviceProfileCache deviceProfileCache;
69 74
70 - public DefaultTbClusterService(TbQueueProducerProvider producerProvider, PartitionService partitionService, DataDecodingEncodingService encodingService) { 75 + public DefaultTbClusterService(TbQueueProducerProvider producerProvider, PartitionService partitionService, DataDecodingEncodingService encodingService, TbDeviceProfileCache deviceProfileCache) {
71 this.producerProvider = producerProvider; 76 this.producerProvider = producerProvider;
72 this.partitionService = partitionService; 77 this.partitionService = partitionService;
73 this.encodingService = encodingService; 78 this.encodingService = encodingService;
  79 + this.deviceProfileCache = deviceProfileCache;
74 } 80 }
75 81
76 @Override 82 @Override
@@ -126,6 +132,12 @@ public class DefaultTbClusterService implements TbClusterService { @@ -126,6 +132,12 @@ public class DefaultTbClusterService implements TbClusterService {
126 log.warn("[{}][{}] Received invalid message: {}", tenantId, entityId, tbMsg); 132 log.warn("[{}][{}] Received invalid message: {}", tenantId, entityId, tbMsg);
127 return; 133 return;
128 } 134 }
  135 + } else {
  136 + if (entityId.getEntityType().equals(EntityType.DEVICE)) {
  137 + tbMsg = transformMsg(tbMsg, deviceProfileCache.get(tenantId, new DeviceId(entityId.getId())));
  138 + } else if (entityId.getEntityType().equals(EntityType.DEVICE_PROFILE)) {
  139 + tbMsg = transformMsg(tbMsg, deviceProfileCache.get(tenantId, new DeviceProfileId(entityId.getId())));
  140 + }
129 } 141 }
130 TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_RULE_ENGINE, tenantId, entityId); 142 TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_RULE_ENGINE, tenantId, entityId);
131 log.trace("PUSHING msg: {} to:{}", tbMsg, tpi); 143 log.trace("PUSHING msg: {} to:{}", tbMsg, tpi);
@@ -137,6 +149,16 @@ public class DefaultTbClusterService implements TbClusterService { @@ -137,6 +149,16 @@ public class DefaultTbClusterService implements TbClusterService {
137 toRuleEngineMsgs.incrementAndGet(); 149 toRuleEngineMsgs.incrementAndGet();
138 } 150 }
139 151
  152 + private TbMsg transformMsg(TbMsg tbMsg, DeviceProfile deviceProfile) {
  153 + if (deviceProfile != null) {
  154 + RuleChainId targetRuleChainId = deviceProfile.getDefaultRuleChainId();
  155 + if (targetRuleChainId != null && !targetRuleChainId.equals(tbMsg.getRuleChainId())) {
  156 + tbMsg = TbMsg.transformMsg(tbMsg, targetRuleChainId);
  157 + }
  158 + }
  159 + return tbMsg;
  160 + }
  161 +
140 @Override 162 @Override
141 public void pushNotificationToRuleEngine(String serviceId, FromDeviceRpcResponse response, TbQueueCallback callback) { 163 public void pushNotificationToRuleEngine(String serviceId, FromDeviceRpcResponse response, TbQueueCallback callback) {
142 TopicPartitionInfo tpi = partitionService.getNotificationsTopic(ServiceType.TB_RULE_ENGINE, serviceId); 164 TopicPartitionInfo tpi = partitionService.getNotificationsTopic(ServiceType.TB_RULE_ENGINE, serviceId);
@@ -167,11 +189,27 @@ public class DefaultTbClusterService implements TbClusterService { @@ -167,11 +189,27 @@ public class DefaultTbClusterService implements TbClusterService {
167 189
168 @Override 190 @Override
169 public void onDeviceProfileChange(DeviceProfile deviceProfile, TbQueueCallback callback) { 191 public void onDeviceProfileChange(DeviceProfile deviceProfile, TbQueueCallback callback) {
170 - log.trace("[{}][{}] Processing device profile [{}] event", deviceProfile.getTenantId(), deviceProfile.getId(), deviceProfile.getName()); 192 + log.trace("[{}][{}] Processing device profile [{}] change event", deviceProfile.getTenantId(), deviceProfile.getId(), deviceProfile.getName());
  193 + TransportProtos.DeviceProfileUpdateMsg profileUpdateMsg = TransportProtos.DeviceProfileUpdateMsg.newBuilder()
  194 + .setData(ByteString.copyFrom(encodingService.encode(deviceProfile))).build();
  195 + ToTransportMsg transportMsg = ToTransportMsg.newBuilder().setDeviceProfileUpdateMsg(profileUpdateMsg).build();
  196 + broadcast(transportMsg);
  197 + }
  198 +
  199 + @Override
  200 + public void onDeviceProfileDelete(DeviceProfile deviceProfile, TbQueueCallback callback) {
  201 + log.trace("[{}][{}] Processing device profile [{}] delete event", deviceProfile.getTenantId(), deviceProfile.getId(), deviceProfile.getName());
  202 + TransportProtos.DeviceProfileDeleteMsg profileDeleteMsg = TransportProtos.DeviceProfileDeleteMsg.newBuilder()
  203 + .setProfileIdMSB(deviceProfile.getId().getId().getMostSignificantBits())
  204 + .setProfileIdLSB(deviceProfile.getId().getId().getLeastSignificantBits())
  205 + .build();
  206 + ToTransportMsg transportMsg = ToTransportMsg.newBuilder().setDeviceProfileDeleteMsg(profileDeleteMsg).build();
  207 + broadcast(transportMsg);
  208 + }
  209 +
  210 + private void broadcast(ToTransportMsg transportMsg) {
171 TbQueueProducer<TbProtoQueueMsg<ToTransportMsg>> toTransportNfProducer = producerProvider.getTransportNotificationsMsgProducer(); 211 TbQueueProducer<TbProtoQueueMsg<ToTransportMsg>> toTransportNfProducer = producerProvider.getTransportNotificationsMsgProducer();
172 Set<String> tbTransportServices = partitionService.getAllServiceIds(ServiceType.TB_TRANSPORT); 212 Set<String> tbTransportServices = partitionService.getAllServiceIds(ServiceType.TB_TRANSPORT);
173 - TransportProtos.DeviceProfileUpdateMsg profileUpdateMsg = TransportProtos.DeviceProfileUpdateMsg.newBuilder().setData(ByteString.copyFrom(encodingService.encode(deviceProfile))).build();  
174 - ToTransportMsg transportMsg = ToTransportMsg.newBuilder().setDeviceProfileUpdateMsg(profileUpdateMsg).build();  
175 for (String transportServiceId : tbTransportServices) { 213 for (String transportServiceId : tbTransportServices) {
176 TopicPartitionInfo tpi = partitionService.getNotificationsTopic(ServiceType.TB_TRANSPORT, transportServiceId); 214 TopicPartitionInfo tpi = partitionService.getNotificationsTopic(ServiceType.TB_TRANSPORT, transportServiceId);
177 toTransportNfProducer.send(tpi, new TbProtoQueueMsg<>(UUID.randomUUID(), transportMsg), null); 215 toTransportNfProducer.send(tpi, new TbProtoQueueMsg<>(UUID.randomUUID(), transportMsg), null);
@@ -21,10 +21,13 @@ import org.springframework.scheduling.annotation.Scheduled; @@ -21,10 +21,13 @@ import org.springframework.scheduling.annotation.Scheduled;
21 import org.springframework.stereotype.Service; 21 import org.springframework.stereotype.Service;
22 import org.thingsboard.rule.engine.api.RpcError; 22 import org.thingsboard.rule.engine.api.RpcError;
23 import org.thingsboard.server.actors.ActorSystemContext; 23 import org.thingsboard.server.actors.ActorSystemContext;
  24 +import org.thingsboard.server.common.data.EntityType;
24 import org.thingsboard.server.common.data.alarm.Alarm; 25 import org.thingsboard.server.common.data.alarm.Alarm;
  26 +import org.thingsboard.server.common.data.id.DeviceProfileId;
25 import org.thingsboard.server.common.data.id.TenantId; 27 import org.thingsboard.server.common.data.id.TenantId;
26 import org.thingsboard.server.common.msg.MsgType; 28 import org.thingsboard.server.common.msg.MsgType;
27 import org.thingsboard.server.common.msg.TbActorMsg; 29 import org.thingsboard.server.common.msg.TbActorMsg;
  30 +import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg;
28 import org.thingsboard.server.common.msg.queue.ServiceType; 31 import org.thingsboard.server.common.msg.queue.ServiceType;
29 import org.thingsboard.server.common.msg.queue.TbCallback; 32 import org.thingsboard.server.common.msg.queue.TbCallback;
30 import org.thingsboard.server.dao.util.mapping.JacksonUtil; 33 import org.thingsboard.server.dao.util.mapping.JacksonUtil;
@@ -48,6 +51,7 @@ import org.thingsboard.server.queue.provider.TbCoreQueueFactory; @@ -48,6 +51,7 @@ import org.thingsboard.server.queue.provider.TbCoreQueueFactory;
48 import org.thingsboard.server.common.stats.StatsFactory; 51 import org.thingsboard.server.common.stats.StatsFactory;
49 import org.thingsboard.server.queue.util.TbCoreComponent; 52 import org.thingsboard.server.queue.util.TbCoreComponent;
50 import org.thingsboard.server.common.transport.util.DataDecodingEncodingService; 53 import org.thingsboard.server.common.transport.util.DataDecodingEncodingService;
  54 +import org.thingsboard.server.service.profile.TbDeviceProfileCache;
51 import org.thingsboard.server.service.queue.processing.AbstractConsumerService; 55 import org.thingsboard.server.service.queue.processing.AbstractConsumerService;
52 import org.thingsboard.server.service.rpc.FromDeviceRpcResponse; 56 import org.thingsboard.server.service.rpc.FromDeviceRpcResponse;
53 import org.thingsboard.server.service.rpc.TbCoreDeviceRpcService; 57 import org.thingsboard.server.service.rpc.TbCoreDeviceRpcService;
@@ -92,8 +96,8 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService<ToCore @@ -92,8 +96,8 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService<ToCore
92 public DefaultTbCoreConsumerService(TbCoreQueueFactory tbCoreQueueFactory, ActorSystemContext actorContext, 96 public DefaultTbCoreConsumerService(TbCoreQueueFactory tbCoreQueueFactory, ActorSystemContext actorContext,
93 DeviceStateService stateService, TbLocalSubscriptionService localSubscriptionService, 97 DeviceStateService stateService, TbLocalSubscriptionService localSubscriptionService,
94 SubscriptionManagerService subscriptionManagerService, DataDecodingEncodingService encodingService, 98 SubscriptionManagerService subscriptionManagerService, DataDecodingEncodingService encodingService,
95 - TbCoreDeviceRpcService tbCoreDeviceRpcService, StatsFactory statsFactory) {  
96 - super(actorContext, encodingService, tbCoreQueueFactory.createToCoreNotificationsMsgConsumer()); 99 + TbCoreDeviceRpcService tbCoreDeviceRpcService, StatsFactory statsFactory, TbDeviceProfileCache deviceProfileCache) {
  100 + super(actorContext, encodingService, deviceProfileCache, tbCoreQueueFactory.createToCoreNotificationsMsgConsumer());
97 this.mainConsumer = tbCoreQueueFactory.createToCoreMsgConsumer(); 101 this.mainConsumer = tbCoreQueueFactory.createToCoreMsgConsumer();
98 this.stateService = stateService; 102 this.stateService = stateService;
99 this.localSubscriptionService = localSubscriptionService; 103 this.localSubscriptionService = localSubscriptionService;
@@ -211,11 +215,7 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService<ToCore @@ -211,11 +215,7 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService<ToCore
211 log.trace("[{}] Forwarding message to RPC service {}", id, toCoreNotification.getFromDeviceRpcResponse()); 215 log.trace("[{}] Forwarding message to RPC service {}", id, toCoreNotification.getFromDeviceRpcResponse());
212 forwardToCoreRpcService(toCoreNotification.getFromDeviceRpcResponse(), callback); 216 forwardToCoreRpcService(toCoreNotification.getFromDeviceRpcResponse(), callback);
213 } else if (toCoreNotification.getComponentLifecycleMsg() != null && !toCoreNotification.getComponentLifecycleMsg().isEmpty()) { 217 } else if (toCoreNotification.getComponentLifecycleMsg() != null && !toCoreNotification.getComponentLifecycleMsg().isEmpty()) {
214 - Optional<TbActorMsg> actorMsg = encodingService.decode(toCoreNotification.getComponentLifecycleMsg().toByteArray());  
215 - if (actorMsg.isPresent()) {  
216 - log.trace("[{}] Forwarding message to App Actor {}", id, actorMsg.get());  
217 - actorContext.tellWithHighPriority(actorMsg.get());  
218 - } 218 + handleComponentLifecycleMsg(id, toCoreNotification.getComponentLifecycleMsg());
219 callback.onSuccess(); 219 callback.onSuccess();
220 } 220 }
221 if (statsEnabled) { 221 if (statsEnabled) {
@@ -15,6 +15,7 @@ @@ -15,6 +15,7 @@
15 */ 15 */
16 package org.thingsboard.server.service.queue; 16 package org.thingsboard.server.service.queue;
17 17
  18 +import com.google.protobuf.ByteString;
18 import com.google.protobuf.ProtocolStringList; 19 import com.google.protobuf.ProtocolStringList;
19 import lombok.extern.slf4j.Slf4j; 20 import lombok.extern.slf4j.Slf4j;
20 import org.springframework.beans.factory.annotation.Value; 21 import org.springframework.beans.factory.annotation.Value;
@@ -38,6 +39,7 @@ import org.thingsboard.server.queue.settings.TbQueueRuleEngineSettings; @@ -38,6 +39,7 @@ import org.thingsboard.server.queue.settings.TbQueueRuleEngineSettings;
38 import org.thingsboard.server.queue.settings.TbRuleEngineQueueConfiguration; 39 import org.thingsboard.server.queue.settings.TbRuleEngineQueueConfiguration;
39 import org.thingsboard.server.queue.util.TbRuleEngineComponent; 40 import org.thingsboard.server.queue.util.TbRuleEngineComponent;
40 import org.thingsboard.server.common.transport.util.DataDecodingEncodingService; 41 import org.thingsboard.server.common.transport.util.DataDecodingEncodingService;
  42 +import org.thingsboard.server.service.profile.TbDeviceProfileCache;
41 import org.thingsboard.server.service.queue.processing.*; 43 import org.thingsboard.server.service.queue.processing.*;
42 import org.thingsboard.server.service.rpc.FromDeviceRpcResponse; 44 import org.thingsboard.server.service.rpc.FromDeviceRpcResponse;
43 import org.thingsboard.server.service.rpc.TbRuleEngineDeviceRpcService; 45 import org.thingsboard.server.service.rpc.TbRuleEngineDeviceRpcService;
@@ -80,8 +82,8 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService< @@ -80,8 +82,8 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService<
80 TbRuleEngineQueueFactory tbRuleEngineQueueFactory, RuleEngineStatisticsService statisticsService, 82 TbRuleEngineQueueFactory tbRuleEngineQueueFactory, RuleEngineStatisticsService statisticsService,
81 ActorSystemContext actorContext, DataDecodingEncodingService encodingService, 83 ActorSystemContext actorContext, DataDecodingEncodingService encodingService,
82 TbRuleEngineDeviceRpcService tbDeviceRpcService, 84 TbRuleEngineDeviceRpcService tbDeviceRpcService,
83 - StatsFactory statsFactory) {  
84 - super(actorContext, encodingService, tbRuleEngineQueueFactory.createToRuleEngineNotificationsMsgConsumer()); 85 + StatsFactory statsFactory, TbDeviceProfileCache deviceProfileCache) {
  86 + super(actorContext, encodingService, deviceProfileCache, tbRuleEngineQueueFactory.createToRuleEngineNotificationsMsgConsumer());
85 this.statisticsService = statisticsService; 87 this.statisticsService = statisticsService;
86 this.ruleEngineSettings = ruleEngineSettings; 88 this.ruleEngineSettings = ruleEngineSettings;
87 this.tbRuleEngineQueueFactory = tbRuleEngineQueueFactory; 89 this.tbRuleEngineQueueFactory = tbRuleEngineQueueFactory;
@@ -239,11 +241,7 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService< @@ -239,11 +241,7 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService<
239 protected void handleNotification(UUID id, TbProtoQueueMsg<ToRuleEngineNotificationMsg> msg, TbCallback callback) throws Exception { 241 protected void handleNotification(UUID id, TbProtoQueueMsg<ToRuleEngineNotificationMsg> msg, TbCallback callback) throws Exception {
240 ToRuleEngineNotificationMsg nfMsg = msg.getValue(); 242 ToRuleEngineNotificationMsg nfMsg = msg.getValue();
241 if (nfMsg.getComponentLifecycleMsg() != null && !nfMsg.getComponentLifecycleMsg().isEmpty()) { 243 if (nfMsg.getComponentLifecycleMsg() != null && !nfMsg.getComponentLifecycleMsg().isEmpty()) {
242 - Optional<TbActorMsg> actorMsg = encodingService.decode(nfMsg.getComponentLifecycleMsg().toByteArray());  
243 - if (actorMsg.isPresent()) {  
244 - log.trace("[{}] Forwarding message to App Actor {}", id, actorMsg.get());  
245 - actorContext.tellWithHighPriority(actorMsg.get());  
246 - } 244 + handleComponentLifecycleMsg(id, nfMsg.getComponentLifecycleMsg());
247 callback.onSuccess(); 245 callback.onSuccess();
248 } else if (nfMsg.hasFromDeviceRpcResponse()) { 246 } else if (nfMsg.hasFromDeviceRpcResponse()) {
249 TransportProtos.FromDeviceRPCResponseProto proto = nfMsg.getFromDeviceRpcResponse(); 247 TransportProtos.FromDeviceRPCResponseProto proto = nfMsg.getFromDeviceRpcResponse();
@@ -17,6 +17,7 @@ package org.thingsboard.server.service.queue; @@ -17,6 +17,7 @@ package org.thingsboard.server.service.queue;
17 17
18 import org.thingsboard.rule.engine.api.msg.ToDeviceActorNotificationMsg; 18 import org.thingsboard.rule.engine.api.msg.ToDeviceActorNotificationMsg;
19 import org.thingsboard.server.common.data.DeviceProfile; 19 import org.thingsboard.server.common.data.DeviceProfile;
  20 +import org.thingsboard.server.common.data.id.DeviceProfileId;
20 import org.thingsboard.server.common.data.id.EntityId; 21 import org.thingsboard.server.common.data.id.EntityId;
21 import org.thingsboard.server.common.data.id.TenantId; 22 import org.thingsboard.server.common.data.id.TenantId;
22 import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; 23 import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
@@ -52,4 +53,5 @@ public interface TbClusterService { @@ -52,4 +53,5 @@ public interface TbClusterService {
52 53
53 void onDeviceProfileChange(DeviceProfile deviceProfile, TbQueueCallback callback); 54 void onDeviceProfileChange(DeviceProfile deviceProfile, TbQueueCallback callback);
54 55
  56 + void onDeviceProfileDelete(DeviceProfile deviceProfileId, TbQueueCallback callback);
55 } 57 }
@@ -15,23 +15,31 @@ @@ -15,23 +15,31 @@
15 */ 15 */
16 package org.thingsboard.server.service.queue.processing; 16 package org.thingsboard.server.service.queue.processing;
17 17
  18 +import com.google.protobuf.ByteString;
18 import lombok.extern.slf4j.Slf4j; 19 import lombok.extern.slf4j.Slf4j;
19 import org.springframework.boot.context.event.ApplicationReadyEvent; 20 import org.springframework.boot.context.event.ApplicationReadyEvent;
20 import org.springframework.context.ApplicationListener; 21 import org.springframework.context.ApplicationListener;
21 import org.springframework.context.event.EventListener; 22 import org.springframework.context.event.EventListener;
22 import org.thingsboard.common.util.ThingsBoardThreadFactory; 23 import org.thingsboard.common.util.ThingsBoardThreadFactory;
23 import org.thingsboard.server.actors.ActorSystemContext; 24 import org.thingsboard.server.actors.ActorSystemContext;
  25 +import org.thingsboard.server.common.data.EntityType;
  26 +import org.thingsboard.server.common.data.id.DeviceId;
  27 +import org.thingsboard.server.common.data.id.DeviceProfileId;
  28 +import org.thingsboard.server.common.msg.TbActorMsg;
  29 +import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg;
24 import org.thingsboard.server.common.msg.queue.ServiceType; 30 import org.thingsboard.server.common.msg.queue.ServiceType;
25 import org.thingsboard.server.common.msg.queue.TbCallback; 31 import org.thingsboard.server.common.msg.queue.TbCallback;
26 import org.thingsboard.server.queue.TbQueueConsumer; 32 import org.thingsboard.server.queue.TbQueueConsumer;
27 import org.thingsboard.server.queue.common.TbProtoQueueMsg; 33 import org.thingsboard.server.queue.common.TbProtoQueueMsg;
28 import org.thingsboard.server.queue.discovery.PartitionChangeEvent; 34 import org.thingsboard.server.queue.discovery.PartitionChangeEvent;
29 import org.thingsboard.server.common.transport.util.DataDecodingEncodingService; 35 import org.thingsboard.server.common.transport.util.DataDecodingEncodingService;
  36 +import org.thingsboard.server.service.profile.TbDeviceProfileCache;
30 import org.thingsboard.server.service.queue.TbPackCallback; 37 import org.thingsboard.server.service.queue.TbPackCallback;
31 import org.thingsboard.server.service.queue.TbPackProcessingContext; 38 import org.thingsboard.server.service.queue.TbPackProcessingContext;
32 39
33 import javax.annotation.PreDestroy; 40 import javax.annotation.PreDestroy;
34 import java.util.List; 41 import java.util.List;
  42 +import java.util.Optional;
35 import java.util.UUID; 43 import java.util.UUID;
36 import java.util.concurrent.ConcurrentHashMap; 44 import java.util.concurrent.ConcurrentHashMap;
37 import java.util.concurrent.ConcurrentMap; 45 import java.util.concurrent.ConcurrentMap;
@@ -51,12 +59,15 @@ public abstract class AbstractConsumerService<N extends com.google.protobuf.Gene @@ -51,12 +59,15 @@ public abstract class AbstractConsumerService<N extends com.google.protobuf.Gene
51 59
52 protected final ActorSystemContext actorContext; 60 protected final ActorSystemContext actorContext;
53 protected final DataDecodingEncodingService encodingService; 61 protected final DataDecodingEncodingService encodingService;
  62 + protected final TbDeviceProfileCache deviceProfileCache;
54 63
55 protected final TbQueueConsumer<TbProtoQueueMsg<N>> nfConsumer; 64 protected final TbQueueConsumer<TbProtoQueueMsg<N>> nfConsumer;
56 65
57 - public AbstractConsumerService(ActorSystemContext actorContext, DataDecodingEncodingService encodingService, TbQueueConsumer<TbProtoQueueMsg<N>> nfConsumer) { 66 + public AbstractConsumerService(ActorSystemContext actorContext, DataDecodingEncodingService encodingService,
  67 + TbDeviceProfileCache deviceProfileCache, TbQueueConsumer<TbProtoQueueMsg<N>> nfConsumer) {
58 this.actorContext = actorContext; 68 this.actorContext = actorContext;
59 this.encodingService = encodingService; 69 this.encodingService = encodingService;
  70 + this.deviceProfileCache = deviceProfileCache;
60 this.nfConsumer = nfConsumer; 71 this.nfConsumer = nfConsumer;
61 } 72 }
62 73
@@ -126,6 +137,23 @@ public abstract class AbstractConsumerService<N extends com.google.protobuf.Gene @@ -126,6 +137,23 @@ public abstract class AbstractConsumerService<N extends com.google.protobuf.Gene
126 }); 137 });
127 } 138 }
128 139
  140 + protected void handleComponentLifecycleMsg(UUID id, ByteString nfMsg) {
  141 + Optional<TbActorMsg> actorMsgOpt = encodingService.decode(nfMsg.toByteArray());
  142 + if (actorMsgOpt.isPresent()) {
  143 + TbActorMsg actorMsg = actorMsgOpt.get();
  144 + if (actorMsg instanceof ComponentLifecycleMsg) {
  145 + ComponentLifecycleMsg componentLifecycleMsg = (ComponentLifecycleMsg) actorMsg;
  146 + if (EntityType.DEVICE_PROFILE.equals(componentLifecycleMsg.getEntityId().getEntityType())) {
  147 + deviceProfileCache.evict(new DeviceProfileId(componentLifecycleMsg.getEntityId().getId()));
  148 + } else if (EntityType.DEVICE.equals(componentLifecycleMsg.getEntityId().getEntityType())) {
  149 + deviceProfileCache.evict(new DeviceId(componentLifecycleMsg.getEntityId().getId()));
  150 + }
  151 + }
  152 + log.trace("[{}] Forwarding message to App Actor {}", id, actorMsg);
  153 + actorContext.tellWithHighPriority(actorMsg);
  154 + }
  155 + }
  156 +
129 protected abstract void handleNotification(UUID id, TbProtoQueueMsg<N> msg, TbCallback callback) throws Exception; 157 protected abstract void handleNotification(UUID id, TbProtoQueueMsg<N> msg, TbCallback callback) throws Exception;
130 158
131 @PreDestroy 159 @PreDestroy
@@ -26,6 +26,7 @@ import org.thingsboard.server.common.data.id.EntityIdFactory; @@ -26,6 +26,7 @@ import org.thingsboard.server.common.data.id.EntityIdFactory;
26 import org.thingsboard.server.common.data.id.RuleChainId; 26 import org.thingsboard.server.common.data.id.RuleChainId;
27 import org.thingsboard.server.common.data.id.RuleNodeId; 27 import org.thingsboard.server.common.data.id.RuleNodeId;
28 import org.thingsboard.server.common.msg.gen.MsgProtos; 28 import org.thingsboard.server.common.msg.gen.MsgProtos;
  29 +import org.thingsboard.server.common.msg.queue.RuleNodeInfo;
29 import org.thingsboard.server.common.msg.queue.ServiceQueue; 30 import org.thingsboard.server.common.msg.queue.ServiceQueue;
30 import org.thingsboard.server.common.msg.queue.TbMsgCallback; 31 import org.thingsboard.server.common.msg.queue.TbMsgCallback;
31 32
@@ -84,6 +85,11 @@ public final class TbMsg implements Serializable { @@ -84,6 +85,11 @@ public final class TbMsg implements Serializable {
84 data, origMsg.getRuleChainId(), origMsg.getRuleNodeId(), origMsg.getCallback()); 85 data, origMsg.getRuleChainId(), origMsg.getRuleNodeId(), origMsg.getCallback());
85 } 86 }
86 87
  88 + public static TbMsg transformMsg(TbMsg origMsg, RuleChainId ruleChainId) {
  89 + return new TbMsg(origMsg.queueName, origMsg.id, origMsg.ts, origMsg.type, origMsg.originator, origMsg.metaData, origMsg.dataType,
  90 + origMsg.data, ruleChainId, null, origMsg.getCallback());
  91 + }
  92 +
87 public static TbMsg newMsg(TbMsg tbMsg, RuleChainId ruleChainId, RuleNodeId ruleNodeId) { 93 public static TbMsg newMsg(TbMsg tbMsg, RuleChainId ruleChainId, RuleNodeId ruleNodeId) {
88 return new TbMsg(tbMsg.getQueueName(), UUID.randomUUID(), tbMsg.getTs(), tbMsg.getType(), tbMsg.getOriginator(), tbMsg.getMetaData().copy(), 94 return new TbMsg(tbMsg.getQueueName(), UUID.randomUUID(), tbMsg.getTs(), tbMsg.getType(), tbMsg.getOriginator(), tbMsg.getMetaData().copy(),
89 tbMsg.getDataType(), tbMsg.getData(), ruleChainId, ruleNodeId, TbMsgCallback.EMPTY); 95 tbMsg.getDataType(), tbMsg.getData(), ruleChainId, ruleNodeId, TbMsgCallback.EMPTY);
@@ -194,6 +194,11 @@ message DeviceProfileUpdateMsg { @@ -194,6 +194,11 @@ message DeviceProfileUpdateMsg {
194 bytes data = 1; 194 bytes data = 1;
195 } 195 }
196 196
  197 +message DeviceProfileDeleteMsg {
  198 + int64 profileIdMSB = 1;
  199 + int64 profileIdLSB = 2;
  200 +}
  201 +
197 message SessionCloseNotificationProto { 202 message SessionCloseNotificationProto {
198 string message = 1; 203 string message = 1;
199 } 204 }
@@ -485,4 +490,5 @@ message ToTransportMsg { @@ -485,4 +490,5 @@ message ToTransportMsg {
485 ToDeviceRpcRequestMsg toDeviceRequest = 6; 490 ToDeviceRpcRequestMsg toDeviceRequest = 6;
486 ToServerRpcResponseMsg toServerResponse = 7; 491 ToServerRpcResponseMsg toServerResponse = 7;
487 DeviceProfileUpdateMsg deviceProfileUpdateMsg = 8; 492 DeviceProfileUpdateMsg deviceProfileUpdateMsg = 8;
  493 + DeviceProfileDeleteMsg deviceProfileDeleteMsg = 9;
488 } 494 }
@@ -23,7 +23,6 @@ import java.util.Optional; @@ -23,7 +23,6 @@ import java.util.Optional;
23 23
24 public interface TransportProfileCache { 24 public interface TransportProfileCache {
25 25
26 -  
27 DeviceProfile getOrCreate(DeviceProfileId id, ByteString profileBody); 26 DeviceProfile getOrCreate(DeviceProfileId id, ByteString profileBody);
28 27
29 DeviceProfile get(DeviceProfileId id); 28 DeviceProfile get(DeviceProfileId id);
@@ -32,4 +31,6 @@ public interface TransportProfileCache { @@ -32,4 +31,6 @@ public interface TransportProfileCache {
32 31
33 DeviceProfile put(ByteString profileBody); 32 DeviceProfile put(ByteString profileBody);
34 33
  34 + void evict(DeviceProfileId id);
  35 +
35 } 36 }
@@ -74,4 +74,9 @@ public class DefaultTransportProfileCache implements TransportProfileCache { @@ -74,4 +74,9 @@ public class DefaultTransportProfileCache implements TransportProfileCache {
74 return null; 74 return null;
75 } 75 }
76 } 76 }
  77 +
  78 + @Override
  79 + public void evict(DeviceProfileId id) {
  80 + deviceProfiles.remove(id);
  81 + }
77 } 82 }
@@ -629,6 +629,11 @@ public class DefaultTransportService implements TransportService { @@ -629,6 +629,11 @@ public class DefaultTransportService implements TransportService {
629 if (deviceProfile != null) { 629 if (deviceProfile != null) {
630 onProfileUpdate(deviceProfile); 630 onProfileUpdate(deviceProfile);
631 } 631 }
  632 + } else if (toSessionMsg.hasDeviceProfileDeleteMsg()) {
  633 + transportProfileCache.evict(new DeviceProfileId(new UUID(
  634 + toSessionMsg.getDeviceProfileDeleteMsg().getProfileIdMSB(),
  635 + toSessionMsg.getDeviceProfileDeleteMsg().getProfileIdLSB()
  636 + )));
632 } else { 637 } else {
633 //TODO: should we notify the device actor about missed session? 638 //TODO: should we notify the device actor about missed session?
634 log.debug("[{}] Missing session.", sessionId); 639 log.debug("[{}] Missing session.", sessionId);