Commit 43596ec5ce8e60e639cda86bc8414fe17b41587d
Merge branch 'develop/3.2' of github.com:thingsboard/thingsboard into develop/3.2
Showing
19 changed files
with
256 additions
and
87 deletions
... | ... | @@ -355,10 +355,12 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService |
355 | 355 | pageData = tenantService.findTenants(pageLink); |
356 | 356 | for (Tenant tenant : pageData.getData()) { |
357 | 357 | List<EntitySubtype> deviceTypes = deviceService.findDeviceTypesByTenantId(tenant.getId()).get(); |
358 | - deviceProfileService.findOrCreateDefaultDeviceProfile(tenant.getId()); | |
358 | + try { | |
359 | + deviceProfileService.createDefaultDeviceProfile(tenant.getId()); | |
360 | + } catch (Exception e){} | |
359 | 361 | for (EntitySubtype deviceType : deviceTypes) { |
360 | 362 | try { |
361 | - deviceProfileService.createDeviceProfile(tenant.getId(), deviceType.getType()); | |
363 | + deviceProfileService.findOrCreateDeviceProfile(tenant.getId(), deviceType.getType()); | |
362 | 364 | } catch (Exception e) { |
363 | 365 | } |
364 | 366 | } | ... | ... |
... | ... | @@ -186,9 +186,8 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { |
186 | 186 | public void testSaveDeviceWithEmptyType() throws Exception { |
187 | 187 | Device device = new Device(); |
188 | 188 | device.setName("My device"); |
189 | - doPost("/api/device", device) | |
190 | - .andExpect(status().isBadRequest()) | |
191 | - .andExpect(statusReason(containsString("Device type should be specified"))); | |
189 | + Device savedDevice = doPost("/api/device", device, Device.class); | |
190 | + Assert.assertEquals("default", savedDevice.getType()); | |
192 | 191 | } |
193 | 192 | |
194 | 193 | @Test | ... | ... |
... | ... | @@ -121,7 +121,7 @@ public abstract class BaseDeviceProfileControllerTest extends AbstractController |
121 | 121 | Assert.assertNotNull(foundDefaultDeviceProfileInfo.getName()); |
122 | 122 | Assert.assertNotNull(foundDefaultDeviceProfileInfo.getType()); |
123 | 123 | Assert.assertEquals(DeviceProfileType.DEFAULT, foundDefaultDeviceProfileInfo.getType()); |
124 | - Assert.assertEquals("Default", foundDefaultDeviceProfileInfo.getName()); | |
124 | + Assert.assertEquals("default", foundDefaultDeviceProfileInfo.getName()); | |
125 | 125 | } |
126 | 126 | |
127 | 127 | @Test | ... | ... |
... | ... | @@ -27,6 +27,8 @@ public interface DeviceProfileService { |
27 | 27 | |
28 | 28 | DeviceProfile findDeviceProfileById(TenantId tenantId, DeviceProfileId deviceProfileId); |
29 | 29 | |
30 | + DeviceProfile findDeviceProfileByName(TenantId tenantId, String profileName); | |
31 | + | |
30 | 32 | DeviceProfileInfo findDeviceProfileInfoById(TenantId tenantId, DeviceProfileId deviceProfileId); |
31 | 33 | |
32 | 34 | DeviceProfile saveDeviceProfile(DeviceProfile deviceProfile); |
... | ... | @@ -37,12 +39,10 @@ public interface DeviceProfileService { |
37 | 39 | |
38 | 40 | PageData<DeviceProfileInfo> findDeviceProfileInfos(TenantId tenantId, PageLink pageLink); |
39 | 41 | |
40 | - DeviceProfile findOrCreateDefaultDeviceProfile(TenantId tenantId); | |
42 | + DeviceProfile findOrCreateDeviceProfile(TenantId tenantId, String profileName); | |
41 | 43 | |
42 | 44 | DeviceProfile createDefaultDeviceProfile(TenantId tenantId); |
43 | 45 | |
44 | - DeviceProfile createDeviceProfile(TenantId tenantId, String profileName); | |
45 | - | |
46 | 46 | DeviceProfile findDefaultDeviceProfile(TenantId tenantId); |
47 | 47 | |
48 | 48 | DeviceProfileInfo findDefaultDeviceProfileInfo(TenantId tenantId); | ... | ... |
... | ... | @@ -39,6 +39,7 @@ import io.netty.util.concurrent.Future; |
39 | 39 | import io.netty.util.concurrent.GenericFutureListener; |
40 | 40 | import lombok.extern.slf4j.Slf4j; |
41 | 41 | import org.springframework.util.StringUtils; |
42 | +import org.thingsboard.server.common.data.DeviceProfile; | |
42 | 43 | import org.thingsboard.server.common.data.DeviceTransportType; |
43 | 44 | import org.thingsboard.server.common.data.device.profile.MqttTopics; |
44 | 45 | import org.thingsboard.server.common.msg.EncryptionUtil; |
... | ... | @@ -574,7 +575,13 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement |
574 | 575 | try { |
575 | 576 | adaptor.convertToPublish(deviceSessionCtx, rpcResponse).ifPresent(deviceSessionCtx.getChannel()::writeAndFlush); |
576 | 577 | } catch (Exception e) { |
577 | - log.trace("[{}] Failed to convert device RPC commandto MQTT msg", sessionId, e); | |
578 | + log.trace("[{}] Failed to convert device RPC command to MQTT msg", sessionId, e); | |
578 | 579 | } |
579 | 580 | } |
581 | + | |
582 | + @Override | |
583 | + public void onProfileUpdate(DeviceProfile deviceProfile) { | |
584 | + deviceSessionCtx.getDeviceInfo().setDeviceType(deviceProfile.getName()); | |
585 | + sessionInfo = SessionInfoProto.newBuilder().mergeFrom(sessionInfo).setDeviceType(deviceProfile.getName()).build(); | |
586 | + } | |
580 | 587 | } | ... | ... |
... | ... | @@ -16,6 +16,7 @@ |
16 | 16 | package org.thingsboard.server.transport.mqtt.session; |
17 | 17 | |
18 | 18 | import lombok.extern.slf4j.Slf4j; |
19 | +import org.thingsboard.server.common.data.DeviceProfile; | |
19 | 20 | import org.thingsboard.server.common.transport.SessionMsgListener; |
20 | 21 | import org.thingsboard.server.common.transport.auth.TransportDeviceInfo; |
21 | 22 | import org.thingsboard.server.gen.transport.TransportProtos; |
... | ... | @@ -32,7 +33,7 @@ import java.util.concurrent.ConcurrentMap; |
32 | 33 | public class GatewayDeviceSessionCtx extends MqttDeviceAwareSessionContext implements SessionMsgListener { |
33 | 34 | |
34 | 35 | private final GatewaySessionHandler parent; |
35 | - private final SessionInfoProto sessionInfo; | |
36 | + private volatile SessionInfoProto sessionInfo; | |
36 | 37 | |
37 | 38 | public GatewayDeviceSessionCtx(GatewaySessionHandler parent, TransportDeviceInfo deviceInfo, ConcurrentMap<MqttTopicMatcher, Integer> mqttQoSMap) { |
38 | 39 | super(UUID.randomUUID(), mqttQoSMap); |
... | ... | @@ -105,4 +106,10 @@ public class GatewayDeviceSessionCtx extends MqttDeviceAwareSessionContext imple |
105 | 106 | public void onToServerRpcResponse(TransportProtos.ToServerRpcResponseMsg toServerResponse) { |
106 | 107 | // This feature is not supported in the TB IoT Gateway yet. |
107 | 108 | } |
109 | + | |
110 | + @Override | |
111 | + public void onProfileUpdate(DeviceProfile deviceProfile) { | |
112 | + deviceInfo.setDeviceType(deviceProfile.getName()); | |
113 | + sessionInfo = SessionInfoProto.newBuilder().mergeFrom(sessionInfo).setDeviceType(deviceProfile.getName()).build(); | |
114 | + } | |
108 | 115 | } | ... | ... |
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.common.transport; | |
17 | + | |
18 | +import com.google.protobuf.ByteString; | |
19 | +import org.thingsboard.server.common.data.DeviceProfile; | |
20 | +import org.thingsboard.server.common.data.id.DeviceProfileId; | |
21 | + | |
22 | +import java.util.Optional; | |
23 | + | |
24 | +public interface TransportProfileCache { | |
25 | + | |
26 | + | |
27 | + DeviceProfile getOrCreate(DeviceProfileId id, ByteString profileBody); | |
28 | + | |
29 | + DeviceProfile get(DeviceProfileId id); | |
30 | + | |
31 | + void put(DeviceProfile profile); | |
32 | + | |
33 | + DeviceProfile put(ByteString profileBody); | |
34 | + | |
35 | +} | ... | ... |
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.common.transport.service; | |
17 | + | |
18 | +import com.google.protobuf.ByteString; | |
19 | +import lombok.extern.slf4j.Slf4j; | |
20 | +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; | |
21 | +import org.springframework.stereotype.Component; | |
22 | +import org.thingsboard.server.common.data.DeviceProfile; | |
23 | +import org.thingsboard.server.common.data.id.DeviceProfileId; | |
24 | +import org.thingsboard.server.common.transport.TransportProfileCache; | |
25 | +import org.thingsboard.server.common.transport.util.DataDecodingEncodingService; | |
26 | + | |
27 | +import java.util.Optional; | |
28 | +import java.util.concurrent.ConcurrentHashMap; | |
29 | +import java.util.concurrent.ConcurrentMap; | |
30 | + | |
31 | +@Slf4j | |
32 | +@Component | |
33 | +@ConditionalOnExpression("('${service.type:null}'=='monolith' && '${transport.api_enabled:true}'=='true') || '${service.type:null}'=='tb-transport'") | |
34 | +public class DefaultTransportProfileCache implements TransportProfileCache { | |
35 | + | |
36 | + private final ConcurrentMap<DeviceProfileId, DeviceProfile> deviceProfiles = new ConcurrentHashMap<>(); | |
37 | + | |
38 | + private final DataDecodingEncodingService dataDecodingEncodingService; | |
39 | + | |
40 | + public DefaultTransportProfileCache(DataDecodingEncodingService dataDecodingEncodingService) { | |
41 | + this.dataDecodingEncodingService = dataDecodingEncodingService; | |
42 | + } | |
43 | + | |
44 | + @Override | |
45 | + public DeviceProfile getOrCreate(DeviceProfileId id, ByteString profileBody) { | |
46 | + DeviceProfile profile = deviceProfiles.get(id); | |
47 | + if (profile == null) { | |
48 | + Optional<DeviceProfile> deviceProfile = dataDecodingEncodingService.decode(profileBody.toByteArray()); | |
49 | + if (deviceProfile.isPresent()) { | |
50 | + profile = deviceProfile.get(); | |
51 | + deviceProfiles.put(id, profile); | |
52 | + } | |
53 | + } | |
54 | + return profile; | |
55 | + } | |
56 | + | |
57 | + @Override | |
58 | + public DeviceProfile get(DeviceProfileId id) { | |
59 | + return deviceProfiles.get(id); | |
60 | + } | |
61 | + | |
62 | + @Override | |
63 | + public void put(DeviceProfile profile) { | |
64 | + deviceProfiles.put(profile.getId(), profile); | |
65 | + } | |
66 | + | |
67 | + @Override | |
68 | + public DeviceProfile put(ByteString profileBody) { | |
69 | + Optional<DeviceProfile> deviceProfile = dataDecodingEncodingService.decode(profileBody.toByteArray()); | |
70 | + if (deviceProfile.isPresent()) { | |
71 | + put(deviceProfile.get()); | |
72 | + return deviceProfile.get(); | |
73 | + } else { | |
74 | + return null; | |
75 | + } | |
76 | + } | |
77 | +} | ... | ... |
... | ... | @@ -43,6 +43,7 @@ import org.thingsboard.server.common.msg.session.SessionMsgType; |
43 | 43 | import org.thingsboard.server.common.msg.tools.TbRateLimits; |
44 | 44 | import org.thingsboard.server.common.msg.tools.TbRateLimitsException; |
45 | 45 | import org.thingsboard.server.common.transport.SessionMsgListener; |
46 | +import org.thingsboard.server.common.transport.TransportProfileCache; | |
46 | 47 | import org.thingsboard.server.common.transport.TransportService; |
47 | 48 | import org.thingsboard.server.common.transport.TransportServiceCallback; |
48 | 49 | import org.thingsboard.server.common.transport.auth.GetOrCreateDeviceFromGatewayResponse; |
... | ... | @@ -121,8 +122,7 @@ public class DefaultTransportService implements TransportService { |
121 | 122 | private final PartitionService partitionService; |
122 | 123 | private final TbServiceInfoProvider serviceInfoProvider; |
123 | 124 | private final StatsFactory statsFactory; |
124 | - private final DataDecodingEncodingService dataDecodingEncodingService; | |
125 | - | |
125 | + private final TransportProfileCache transportProfileCache; | |
126 | 126 | |
127 | 127 | protected TbQueueRequestTemplate<TbProtoQueueMsg<TransportApiRequestMsg>, TbProtoQueueMsg<TransportApiResponseMsg>> transportApiRequestTemplate; |
128 | 128 | protected TbQueueProducer<TbProtoQueueMsg<ToRuleEngineMsg>> ruleEngineMsgProducer; |
... | ... | @@ -141,7 +141,6 @@ public class DefaultTransportService implements TransportService { |
141 | 141 | //TODO 3.2: @ybondarenko Implement cleanup of this maps. |
142 | 142 | private final ConcurrentMap<TenantId, TbRateLimits> perTenantLimits = new ConcurrentHashMap<>(); |
143 | 143 | private final ConcurrentMap<DeviceId, TbRateLimits> perDeviceLimits = new ConcurrentHashMap<>(); |
144 | - private final ConcurrentMap<DeviceProfileId, DeviceProfile> deviceProfiles = new ConcurrentHashMap<>(); | |
145 | 144 | |
146 | 145 | private ExecutorService mainConsumerExecutor = Executors.newSingleThreadExecutor(ThingsBoardThreadFactory.forName("transport-consumer")); |
147 | 146 | private volatile boolean stopped = false; |
... | ... | @@ -151,13 +150,13 @@ public class DefaultTransportService implements TransportService { |
151 | 150 | TbQueueProducerProvider producerProvider, |
152 | 151 | PartitionService partitionService, |
153 | 152 | StatsFactory statsFactory, |
154 | - DataDecodingEncodingService dataDecodingEncodingService) { | |
153 | + TransportProfileCache transportProfileCache) { | |
155 | 154 | this.serviceInfoProvider = serviceInfoProvider; |
156 | 155 | this.queueProvider = queueProvider; |
157 | 156 | this.producerProvider = producerProvider; |
158 | 157 | this.partitionService = partitionService; |
159 | 158 | this.statsFactory = statsFactory; |
160 | - this.dataDecodingEncodingService = dataDecodingEncodingService; | |
159 | + this.transportProfileCache = transportProfileCache; | |
161 | 160 | } |
162 | 161 | |
163 | 162 | @PostConstruct |
... | ... | @@ -276,14 +275,7 @@ public class DefaultTransportService implements TransportService { |
276 | 275 | result.deviceInfo(tdi); |
277 | 276 | ByteString profileBody = msg.getProfileBody(); |
278 | 277 | if (profileBody != null && !profileBody.isEmpty()) { |
279 | - DeviceProfile profile = deviceProfiles.get(tdi.getDeviceProfileId()); | |
280 | - if (profile == null) { | |
281 | - Optional<DeviceProfile> deviceProfile = dataDecodingEncodingService.decode(profileBody.toByteArray()); | |
282 | - if (deviceProfile.isPresent()) { | |
283 | - profile = deviceProfile.get(); | |
284 | - deviceProfiles.put(tdi.getDeviceProfileId(), profile); | |
285 | - } | |
286 | - } | |
278 | + DeviceProfile profile = transportProfileCache.getOrCreate(tdi.getDeviceProfileId(), profileBody); | |
287 | 279 | if (transportType != DeviceTransportType.DEFAULT |
288 | 280 | && profile != null && profile.getTransportType() != DeviceTransportType.DEFAULT && profile.getTransportType() != transportType) { |
289 | 281 | log.debug("[{}] Device profile [{}] has different transport type: {}, expected: {}", tdi.getDeviceId(), tdi.getDeviceProfileId(), profile.getTransportType(), transportType); |
... | ... | @@ -309,15 +301,7 @@ public class DefaultTransportService implements TransportService { |
309 | 301 | result.deviceInfo(tdi); |
310 | 302 | ByteString profileBody = msg.getProfileBody(); |
311 | 303 | if (profileBody != null && !profileBody.isEmpty()) { |
312 | - DeviceProfile profile = deviceProfiles.get(tdi.getDeviceProfileId()); | |
313 | - if (profile == null) { | |
314 | - Optional<DeviceProfile> deviceProfile = dataDecodingEncodingService.decode(profileBody.toByteArray()); | |
315 | - if (deviceProfile.isPresent()) { | |
316 | - profile = deviceProfile.get(); | |
317 | - deviceProfiles.put(tdi.getDeviceProfileId(), profile); | |
318 | - } | |
319 | - } | |
320 | - result.deviceProfile(profile); | |
304 | + result.deviceProfile(transportProfileCache.getOrCreate(tdi.getDeviceProfileId(), profileBody)); | |
321 | 305 | } |
322 | 306 | } |
323 | 307 | return result.build(); |
... | ... | @@ -629,8 +613,10 @@ public class DefaultTransportService implements TransportService { |
629 | 613 | } |
630 | 614 | } else { |
631 | 615 | if (toSessionMsg.hasDeviceProfileUpdateMsg()) { |
632 | - Optional<DeviceProfile> deviceProfile = dataDecodingEncodingService.decode(toSessionMsg.getDeviceProfileUpdateMsg().getData().toByteArray()); | |
633 | - deviceProfile.ifPresent(this::onProfileUpdate); | |
616 | + DeviceProfile deviceProfile = transportProfileCache.put(toSessionMsg.getDeviceProfileUpdateMsg().getData()); | |
617 | + if (deviceProfile != null) { | |
618 | + onProfileUpdate(deviceProfile); | |
619 | + } | |
634 | 620 | } else { |
635 | 621 | //TODO: should we notify the device actor about missed session? |
636 | 622 | log.debug("[{}] Missing session.", sessionId); |
... | ... | @@ -640,7 +626,7 @@ public class DefaultTransportService implements TransportService { |
640 | 626 | |
641 | 627 | @Override |
642 | 628 | public void getDeviceProfile(DeviceProfileId deviceProfileId, TransportServiceCallback<DeviceProfile> callback) { |
643 | - DeviceProfile deviceProfile = deviceProfiles.get(deviceProfileId); | |
629 | + DeviceProfile deviceProfile = transportProfileCache.get(deviceProfileId); | |
644 | 630 | if (deviceProfile != null) { |
645 | 631 | callback.onSuccess(deviceProfile); |
646 | 632 | } else { |
... | ... | @@ -653,14 +639,13 @@ public class DefaultTransportService implements TransportService { |
653 | 639 | TransportApiRequestMsg.newBuilder().setGetDeviceProfileRequestMsg(msg).build()); |
654 | 640 | AsyncCallbackTemplate.withCallback(transportApiRequestTemplate.send(protoMsg), |
655 | 641 | response -> { |
656 | - byte[] devProfileBody = response.getValue().getGetDeviceProfileResponseMsg().getData().toByteArray(); | |
657 | - if (devProfileBody != null && devProfileBody.length > 0) { | |
658 | - Optional<DeviceProfile> deviceProfileOpt = dataDecodingEncodingService.decode(devProfileBody); | |
659 | - if (deviceProfileOpt.isPresent()) { | |
660 | - deviceProfiles.put(deviceProfileOpt.get().getId(), deviceProfile); | |
661 | - callback.onSuccess(deviceProfileOpt.get()); | |
642 | + ByteString devProfileBody = response.getValue().getGetDeviceProfileResponseMsg().getData(); | |
643 | + if (devProfileBody != null && !devProfileBody.isEmpty()) { | |
644 | + DeviceProfile profile = transportProfileCache.put(devProfileBody); | |
645 | + if (profile != null) { | |
646 | + callback.onSuccess(profile); | |
662 | 647 | } else { |
663 | - log.warn("Failed to decode device profile: {}", Arrays.toString(devProfileBody)); | |
648 | + log.warn("Failed to decode device profile: {}", devProfileBody); | |
664 | 649 | callback.onError(new IllegalArgumentException("Failed to decode device profile!")); |
665 | 650 | } |
666 | 651 | } else { |
... | ... | @@ -673,7 +658,6 @@ public class DefaultTransportService implements TransportService { |
673 | 658 | |
674 | 659 | @Override |
675 | 660 | public void onProfileUpdate(DeviceProfile deviceProfile) { |
676 | - deviceProfiles.put(deviceProfile.getId(), deviceProfile); | |
677 | 661 | long deviceProfileIdMSB = deviceProfile.getId().getId().getMostSignificantBits(); |
678 | 662 | long deviceProfileIdLSB = deviceProfile.getId().getId().getLeastSignificantBits(); |
679 | 663 | sessions.forEach((id, md) -> { |
... | ... | @@ -736,7 +720,7 @@ public class DefaultTransportService implements TransportService { |
736 | 720 | |
737 | 721 | private RuleChainId resolveRuleChainId(TransportProtos.SessionInfoProto sessionInfo) { |
738 | 722 | DeviceProfileId deviceProfileId = new DeviceProfileId(new UUID(sessionInfo.getDeviceProfileIdMSB(), sessionInfo.getDeviceProfileIdLSB())); |
739 | - DeviceProfile deviceProfile = deviceProfiles.get(deviceProfileId); | |
723 | + DeviceProfile deviceProfile = transportProfileCache.get(deviceProfileId); | |
740 | 724 | RuleChainId ruleChainId; |
741 | 725 | if (deviceProfile == null) { |
742 | 726 | log.warn("[{}] Device profile is null!", deviceProfileId); |
... | ... | @@ -747,27 +731,6 @@ public class DefaultTransportService implements TransportService { |
747 | 731 | return ruleChainId; |
748 | 732 | } |
749 | 733 | |
750 | - private <T extends com.google.protobuf.GeneratedMessageV3> ListenableFuture<TbProtoQueueMsg<T>> extractProfile(ListenableFuture<TbProtoQueueMsg<T>> send, | |
751 | - Function<T, Boolean> hasDeviceInfo, | |
752 | - Function<T, TransportProtos.DeviceInfoProto> deviceInfoF, | |
753 | - Function<T, ByteString> profileBodyF) { | |
754 | - return Futures.transform(send, response -> { | |
755 | - T value = response.getValue(); | |
756 | - if (hasDeviceInfo.apply(value)) { | |
757 | - TransportProtos.DeviceInfoProto deviceInfo = deviceInfoF.apply(value); | |
758 | - ByteString profileBody = profileBodyF.apply(value); | |
759 | - if (profileBody != null && !profileBody.isEmpty()) { | |
760 | - DeviceProfileId deviceProfileId = new DeviceProfileId(new UUID(deviceInfo.getDeviceProfileIdMSB(), deviceInfo.getDeviceProfileIdLSB())); | |
761 | - if (!deviceProfiles.containsKey(deviceProfileId)) { | |
762 | - Optional<DeviceProfile> deviceProfile = dataDecodingEncodingService.decode(profileBody.toByteArray()); | |
763 | - deviceProfile.ifPresent(profile -> deviceProfiles.put(deviceProfileId, profile)); | |
764 | - } | |
765 | - } | |
766 | - } | |
767 | - return response; | |
768 | - }, transportCallbackExecutor); | |
769 | - } | |
770 | - | |
771 | 734 | private class TransportTbQueueCallback implements TbQueueCallback { |
772 | 735 | private final TransportServiceCallback<Void> callback; |
773 | 736 | ... | ... |
... | ... | @@ -35,7 +35,7 @@ public abstract class DeviceAwareSessionContext implements SessionContext { |
35 | 35 | @Getter |
36 | 36 | private volatile DeviceId deviceId; |
37 | 37 | @Getter |
38 | - private volatile TransportDeviceInfo deviceInfo; | |
38 | + protected volatile TransportDeviceInfo deviceInfo; | |
39 | 39 | private volatile boolean connected; |
40 | 40 | |
41 | 41 | public DeviceId getDeviceId() { | ... | ... |
... | ... | @@ -184,4 +184,15 @@ public interface DeviceDao extends Dao<Device> { |
184 | 184 | ListenableFuture<Device> findDeviceByTenantIdAndIdAsync(TenantId tenantId, UUID id); |
185 | 185 | |
186 | 186 | Long countDevicesByDeviceProfileId(TenantId tenantId, UUID deviceProfileId); |
187 | + | |
188 | + /** | |
189 | + * Find devices by tenantId, profileId and page link. | |
190 | + * | |
191 | + * @param tenantId the tenantId | |
192 | + * @param profileId the profileId | |
193 | + * @param pageLink the page link | |
194 | + * @return the list of device objects | |
195 | + */ | |
196 | + PageData<Device> findDevicesByTenantIdAndProfileId(UUID tenantId, UUID profileId, PageLink pageLink); | |
197 | + | |
187 | 198 | } | ... | ... |
... | ... | @@ -37,4 +37,6 @@ public interface DeviceProfileDao extends Dao<DeviceProfile> { |
37 | 37 | DeviceProfile findDefaultDeviceProfile(TenantId tenantId); |
38 | 38 | |
39 | 39 | DeviceProfileInfo findDefaultDeviceProfileInfo(TenantId tenantId); |
40 | + | |
41 | + DeviceProfile findByName(TenantId tenantId, String profileName); | |
40 | 42 | } | ... | ... |
... | ... | @@ -23,10 +23,12 @@ import org.springframework.cache.Cache; |
23 | 23 | import org.springframework.cache.CacheManager; |
24 | 24 | import org.springframework.cache.annotation.Cacheable; |
25 | 25 | import org.springframework.stereotype.Service; |
26 | +import org.thingsboard.server.common.data.Device; | |
26 | 27 | import org.thingsboard.server.common.data.DeviceProfile; |
27 | 28 | import org.thingsboard.server.common.data.DeviceProfileInfo; |
28 | 29 | import org.thingsboard.server.common.data.DeviceProfileType; |
29 | 30 | import org.thingsboard.server.common.data.DeviceTransportType; |
31 | +import org.thingsboard.server.common.data.EntitySubtype; | |
30 | 32 | import org.thingsboard.server.common.data.Tenant; |
31 | 33 | import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileConfiguration; |
32 | 34 | import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileTransportConfiguration; |
... | ... | @@ -44,6 +46,7 @@ import org.thingsboard.server.dao.tenant.TenantDao; |
44 | 46 | |
45 | 47 | import java.util.Arrays; |
46 | 48 | import java.util.Collections; |
49 | +import java.util.List; | |
47 | 50 | |
48 | 51 | import static org.thingsboard.server.common.data.CacheConstants.DEVICE_PROFILE_CACHE; |
49 | 52 | import static org.thingsboard.server.dao.service.Validator.validateId; |
... | ... | @@ -54,6 +57,7 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D |
54 | 57 | |
55 | 58 | private static final String INCORRECT_TENANT_ID = "Incorrect tenantId "; |
56 | 59 | private static final String INCORRECT_DEVICE_PROFILE_ID = "Incorrect deviceProfileId "; |
60 | + private static final String INCORRECT_DEVICE_PROFILE_NAME = "Incorrect deviceProfileName "; | |
57 | 61 | |
58 | 62 | @Autowired |
59 | 63 | private DeviceProfileDao deviceProfileDao; |
... | ... | @@ -62,6 +66,9 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D |
62 | 66 | private DeviceDao deviceDao; |
63 | 67 | |
64 | 68 | @Autowired |
69 | + private DeviceService deviceService; | |
70 | + | |
71 | + @Autowired | |
65 | 72 | private TenantDao tenantDao; |
66 | 73 | |
67 | 74 | @Autowired |
... | ... | @@ -75,6 +82,13 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D |
75 | 82 | return deviceProfileDao.findById(tenantId, deviceProfileId.getId()); |
76 | 83 | } |
77 | 84 | |
85 | + @Override | |
86 | + public DeviceProfile findDeviceProfileByName(TenantId tenantId, String profileName) { | |
87 | + log.trace("Executing findDeviceProfileByName [{}][{}]", tenantId, profileName); | |
88 | + Validator.validateString(profileName, INCORRECT_DEVICE_PROFILE_NAME + profileName); | |
89 | + return deviceProfileDao.findByName(tenantId, profileName); | |
90 | + } | |
91 | + | |
78 | 92 | @Cacheable(cacheNames = DEVICE_PROFILE_CACHE, key = "{'info', #deviceProfileId.id}") |
79 | 93 | @Override |
80 | 94 | public DeviceProfileInfo findDeviceProfileInfoById(TenantId tenantId, DeviceProfileId deviceProfileId) { |
... | ... | @@ -87,6 +101,10 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D |
87 | 101 | public DeviceProfile saveDeviceProfile(DeviceProfile deviceProfile) { |
88 | 102 | log.trace("Executing saveDeviceProfile [{}]", deviceProfile); |
89 | 103 | deviceProfileValidator.validate(deviceProfile, DeviceProfile::getTenantId); |
104 | + DeviceProfile oldDeviceProfile = null; | |
105 | + if (deviceProfile.getId() != null) { | |
106 | + oldDeviceProfile = deviceProfileDao.findById(deviceProfile.getTenantId(), deviceProfile.getId().getId()); | |
107 | + } | |
90 | 108 | DeviceProfile savedDeviceProfile; |
91 | 109 | try { |
92 | 110 | savedDeviceProfile = deviceProfileDao.save(deviceProfile.getTenantId(), deviceProfile); |
... | ... | @@ -101,10 +119,23 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D |
101 | 119 | Cache cache = cacheManager.getCache(DEVICE_PROFILE_CACHE); |
102 | 120 | cache.evict(Collections.singletonList(savedDeviceProfile.getId().getId())); |
103 | 121 | cache.evict(Arrays.asList("info", savedDeviceProfile.getId().getId())); |
122 | + cache.evict(Arrays.asList(deviceProfile.getTenantId().getId(), deviceProfile.getName())); | |
104 | 123 | if (savedDeviceProfile.isDefault()) { |
105 | 124 | cache.evict(Arrays.asList("default", savedDeviceProfile.getTenantId().getId())); |
106 | 125 | cache.evict(Arrays.asList("default", "info", savedDeviceProfile.getTenantId().getId())); |
107 | 126 | } |
127 | + if (oldDeviceProfile != null && !oldDeviceProfile.getName().equals(deviceProfile.getName())) { | |
128 | + PageLink pageLink = new PageLink(100); | |
129 | + PageData<Device> pageData; | |
130 | + do { | |
131 | + pageData = deviceDao.findDevicesByTenantIdAndProfileId(deviceProfile.getTenantId().getId(), deviceProfile.getUuidId(), pageLink); | |
132 | + for (Device device : pageData.getData()) { | |
133 | + device.setType(deviceProfile.getName()); | |
134 | + deviceService.saveDevice(device); | |
135 | + } | |
136 | + pageLink = pageLink.nextPageLink(); | |
137 | + } while (pageData.hasNext()); | |
138 | + } | |
108 | 139 | return savedDeviceProfile; |
109 | 140 | } |
110 | 141 | |
... | ... | @@ -116,10 +147,11 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D |
116 | 147 | if (deviceProfile != null && deviceProfile.isDefault()) { |
117 | 148 | throw new DataValidationException("Deletion of Default Device Profile is prohibited!"); |
118 | 149 | } |
119 | - this.removeDeviceProfile(tenantId, deviceProfileId); | |
150 | + this.removeDeviceProfile(tenantId, deviceProfile); | |
120 | 151 | } |
121 | 152 | |
122 | - private void removeDeviceProfile(TenantId tenantId, DeviceProfileId deviceProfileId) { | |
153 | + private void removeDeviceProfile(TenantId tenantId, DeviceProfile deviceProfile) { | |
154 | + DeviceProfileId deviceProfileId = deviceProfile.getId(); | |
123 | 155 | try { |
124 | 156 | deviceProfileDao.removeById(tenantId, deviceProfileId.getId()); |
125 | 157 | } catch (Exception t) { |
... | ... | @@ -134,6 +166,7 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D |
134 | 166 | Cache cache = cacheManager.getCache(DEVICE_PROFILE_CACHE); |
135 | 167 | cache.evict(Collections.singletonList(deviceProfileId.getId())); |
136 | 168 | cache.evict(Arrays.asList("info", deviceProfileId.getId())); |
169 | + cache.evict(Arrays.asList(tenantId.getId(), deviceProfile.getName())); | |
137 | 170 | } |
138 | 171 | |
139 | 172 | @Override |
... | ... | @@ -152,12 +185,13 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D |
152 | 185 | return deviceProfileDao.findDeviceProfileInfos(tenantId, pageLink); |
153 | 186 | } |
154 | 187 | |
188 | + @Cacheable(cacheNames = DEVICE_PROFILE_CACHE, key = "{#tenantId.id, #name}") | |
155 | 189 | @Override |
156 | - public DeviceProfile findOrCreateDefaultDeviceProfile(TenantId tenantId) { | |
190 | + public DeviceProfile findOrCreateDeviceProfile(TenantId tenantId, String name) { | |
157 | 191 | log.trace("Executing findOrCreateDefaultDeviceProfile"); |
158 | - DeviceProfile deviceProfile = findDefaultDeviceProfile(tenantId); | |
192 | + DeviceProfile deviceProfile = findDeviceProfileByName(tenantId, name); | |
159 | 193 | if (deviceProfile == null) { |
160 | - deviceProfile = this.createDefaultDeviceProfile(tenantId); | |
194 | + deviceProfile = this.doCreateDefaultDeviceProfile(tenantId, name, name.equals("default")); | |
161 | 195 | } |
162 | 196 | return deviceProfile; |
163 | 197 | } |
... | ... | @@ -168,12 +202,6 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D |
168 | 202 | return doCreateDefaultDeviceProfile(tenantId, "default", true); |
169 | 203 | } |
170 | 204 | |
171 | - @Override | |
172 | - public DeviceProfile createDeviceProfile(TenantId tenantId, String profileName) { | |
173 | - log.trace("Executing createDefaultDeviceProfile tenantId [{}], profileName [{}]", tenantId, profileName); | |
174 | - return doCreateDefaultDeviceProfile(tenantId, profileName, false); | |
175 | - } | |
176 | - | |
177 | 205 | private DeviceProfile doCreateDefaultDeviceProfile(TenantId tenantId, String profileName, boolean defaultProfile) { |
178 | 206 | validateId(tenantId, INCORRECT_TENANT_ID + tenantId); |
179 | 207 | DeviceProfile deviceProfile = new DeviceProfile(); |
... | ... | @@ -227,6 +255,7 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D |
227 | 255 | deviceProfileDao.save(tenantId, deviceProfile); |
228 | 256 | cache.evict(Collections.singletonList(previousDefaultDeviceProfile.getId().getId())); |
229 | 257 | cache.evict(Arrays.asList("info", previousDefaultDeviceProfile.getId().getId())); |
258 | + cache.evict(Arrays.asList(tenantId.getId(), previousDefaultDeviceProfile.getName())); | |
230 | 259 | changed = true; |
231 | 260 | } |
232 | 261 | if (changed) { |
... | ... | @@ -234,6 +263,7 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D |
234 | 263 | cache.evict(Arrays.asList("info", deviceProfile.getId().getId())); |
235 | 264 | cache.evict(Arrays.asList("default", tenantId.getId())); |
236 | 265 | cache.evict(Arrays.asList("default", "info", tenantId.getId())); |
266 | + cache.evict(Arrays.asList(tenantId.getId(), deviceProfile.getName())); | |
237 | 267 | } |
238 | 268 | return changed; |
239 | 269 | } |
... | ... | @@ -309,7 +339,7 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D |
309 | 339 | |
310 | 340 | @Override |
311 | 341 | protected void removeEntity(TenantId tenantId, DeviceProfile entity) { |
312 | - removeDeviceProfile(tenantId, entity.getId()); | |
342 | + removeDeviceProfile(tenantId, entity); | |
313 | 343 | } |
314 | 344 | }; |
315 | 345 | ... | ... |
... | ... | @@ -169,8 +169,14 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe |
169 | 169 | deviceValidator.validate(device, Device::getTenantId); |
170 | 170 | Device savedDevice; |
171 | 171 | try { |
172 | + DeviceProfile deviceProfile; | |
172 | 173 | if (device.getDeviceProfileId() == null) { |
173 | - DeviceProfile deviceProfile = this.deviceProfileService.findOrCreateDefaultDeviceProfile(device.getTenantId()); | |
174 | + if (!StringUtils.isEmpty(device.getType())) { | |
175 | + deviceProfile = this.deviceProfileService.findOrCreateDeviceProfile(device.getTenantId(), device.getType()); | |
176 | + } else { | |
177 | + deviceProfile = this.deviceProfileService.findDefaultDeviceProfile(device.getTenantId()); | |
178 | + device.setType(deviceProfile.getName()); | |
179 | + } | |
174 | 180 | device.setDeviceProfileId(new DeviceProfileId(deviceProfile.getId().getId())); |
175 | 181 | DeviceData deviceData = new DeviceData(); |
176 | 182 | switch (deviceProfile.getType()) { |
... | ... | @@ -178,7 +184,7 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe |
178 | 184 | deviceData.setConfiguration(new DefaultDeviceConfiguration()); |
179 | 185 | break; |
180 | 186 | } |
181 | - switch (deviceProfile.getTransportType()){ | |
187 | + switch (deviceProfile.getTransportType()) { | |
182 | 188 | case DEFAULT: |
183 | 189 | deviceData.setTransportConfiguration(new DefaultDeviceTransportConfiguration()); |
184 | 190 | break; |
... | ... | @@ -190,7 +196,14 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe |
190 | 196 | break; |
191 | 197 | } |
192 | 198 | device.setDeviceData(deviceData); |
199 | + } else { | |
200 | + deviceProfile = this.deviceProfileService.findDeviceProfileById(device.getTenantId(), device.getDeviceProfileId()); | |
201 | + if (deviceProfile == null) { | |
202 | + throw new DataValidationException("Device is referencing non existing device profile!"); | |
203 | + } | |
193 | 204 | } |
205 | + device.setType(deviceProfile.getName()); | |
206 | + | |
194 | 207 | savedDevice = deviceDao.save(device.getTenantId(), device); |
195 | 208 | } catch (Exception t) { |
196 | 209 | ConstraintViolationException e = extractConstraintViolationException(t).orElse(null); |
... | ... | @@ -441,9 +454,6 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe |
441 | 454 | |
442 | 455 | @Override |
443 | 456 | protected void validateDataImpl(TenantId tenantId, Device device) { |
444 | - if (StringUtils.isEmpty(device.getType())) { | |
445 | - throw new DataValidationException("Device type should be specified!"); | |
446 | - } | |
447 | 457 | if (StringUtils.isEmpty(device.getName())) { |
448 | 458 | throw new DataValidationException("Device name should be specified!"); |
449 | 459 | } | ... | ... |
... | ... | @@ -20,6 +20,7 @@ import org.springframework.data.domain.Pageable; |
20 | 20 | import org.springframework.data.jpa.repository.Query; |
21 | 21 | import org.springframework.data.repository.PagingAndSortingRepository; |
22 | 22 | import org.springframework.data.repository.query.Param; |
23 | +import org.thingsboard.server.common.data.DeviceProfile; | |
23 | 24 | import org.thingsboard.server.common.data.DeviceProfileInfo; |
24 | 25 | import org.thingsboard.server.dao.model.sql.DeviceProfileEntity; |
25 | 26 | |
... | ... | @@ -53,4 +54,7 @@ public interface DeviceProfileRepository extends PagingAndSortingRepository<Devi |
53 | 54 | "FROM DeviceProfileEntity d " + |
54 | 55 | "WHERE d.tenantId = :tenantId AND d.isDefault = true") |
55 | 56 | DeviceProfileInfo findDefaultDeviceProfileInfo(@Param("tenantId") UUID tenantId); |
57 | + | |
58 | + DeviceProfileEntity findByTenantIdAndName(UUID id, String profileName); | |
59 | + | |
56 | 60 | } | ... | ... |
... | ... | @@ -46,6 +46,14 @@ public interface DeviceRepository extends PagingAndSortingRepository<DeviceEntit |
46 | 46 | @Param("searchText") String searchText, |
47 | 47 | Pageable pageable); |
48 | 48 | |
49 | + @Query("SELECT d FROM DeviceEntity d WHERE d.tenantId = :tenantId " + | |
50 | + "AND d.deviceProfileId = :profileId " + | |
51 | + "AND LOWER(d.searchText) LIKE LOWER(CONCAT(:searchText, '%'))") | |
52 | + Page<DeviceEntity> findByTenantIdAndProfileId(@Param("tenantId") UUID tenantId, | |
53 | + @Param("profileId") UUID profileId, | |
54 | + @Param("searchText") String searchText, | |
55 | + Pageable pageable); | |
56 | + | |
49 | 57 | @Query("SELECT new org.thingsboard.server.dao.model.sql.DeviceInfoEntity(d, c.title, c.additionalInfo, p.name) " + |
50 | 58 | "FROM DeviceEntity d " + |
51 | 59 | "LEFT JOIN CustomerEntity c on c.id = d.customerId " + | ... | ... |
... | ... | @@ -105,6 +105,16 @@ public class JpaDeviceDao extends JpaAbstractSearchTextDao<DeviceEntity, Device> |
105 | 105 | } |
106 | 106 | |
107 | 107 | @Override |
108 | + public PageData<Device> findDevicesByTenantIdAndProfileId(UUID tenantId, UUID profileId, PageLink pageLink) { | |
109 | + return DaoUtil.toPageData( | |
110 | + deviceRepository.findByTenantIdAndProfileId( | |
111 | + tenantId, | |
112 | + profileId, | |
113 | + Objects.toString(pageLink.getTextSearch(), ""), | |
114 | + DaoUtil.toPageable(pageLink))); | |
115 | + } | |
116 | + | |
117 | + @Override | |
108 | 118 | public PageData<DeviceInfo> findDeviceInfosByTenantIdAndCustomerId(UUID tenantId, UUID customerId, PageLink pageLink) { |
109 | 119 | return DaoUtil.toPageData( |
110 | 120 | deviceRepository.findDeviceInfosByTenantIdAndCustomerId( | ... | ... |
... | ... | @@ -80,4 +80,8 @@ public class JpaDeviceProfileDao extends JpaAbstractSearchTextDao<DeviceProfileE |
80 | 80 | return deviceProfileRepository.findDefaultDeviceProfileInfo(tenantId.getId()); |
81 | 81 | } |
82 | 82 | |
83 | + @Override | |
84 | + public DeviceProfile findByName(TenantId tenantId, String profileName) { | |
85 | + return DaoUtil.getData(deviceProfileRepository.findByTenantIdAndName(tenantId.getId(), profileName)); | |
86 | + } | |
83 | 87 | } | ... | ... |
... | ... | @@ -270,7 +270,7 @@ public abstract class BaseDeviceServiceTest extends AbstractServiceTest { |
270 | 270 | name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase(); |
271 | 271 | device.setName(name); |
272 | 272 | device.setType("default"); |
273 | - devicesTitle1.add(new DeviceInfo(deviceService.saveDevice(device), null, false, "Default")); | |
273 | + devicesTitle1.add(new DeviceInfo(deviceService.saveDevice(device), null, false, "default")); | |
274 | 274 | } |
275 | 275 | String title2 = "Device title 2"; |
276 | 276 | List<DeviceInfo> devicesTitle2 = new ArrayList<>(); |
... | ... | @@ -282,7 +282,7 @@ public abstract class BaseDeviceServiceTest extends AbstractServiceTest { |
282 | 282 | name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase(); |
283 | 283 | device.setName(name); |
284 | 284 | device.setType("default"); |
285 | - devicesTitle2.add(new DeviceInfo(deviceService.saveDevice(device), null, false, "Default")); | |
285 | + devicesTitle2.add(new DeviceInfo(deviceService.saveDevice(device), null, false, "default")); | |
286 | 286 | } |
287 | 287 | |
288 | 288 | List<DeviceInfo> loadedDevicesTitle1 = new ArrayList<>(); |
... | ... | @@ -435,7 +435,7 @@ public abstract class BaseDeviceServiceTest extends AbstractServiceTest { |
435 | 435 | device.setName("Device"+i); |
436 | 436 | device.setType("default"); |
437 | 437 | device = deviceService.saveDevice(device); |
438 | - devices.add(new DeviceInfo(deviceService.assignDeviceToCustomer(tenantId, device.getId(), customerId), customer.getTitle(), customer.isPublic(), "Default")); | |
438 | + devices.add(new DeviceInfo(deviceService.assignDeviceToCustomer(tenantId, device.getId(), customerId), customer.getTitle(), customer.isPublic(), "default")); | |
439 | 439 | } |
440 | 440 | |
441 | 441 | List<DeviceInfo> loadedDevices = new ArrayList<>(); | ... | ... |