diff --git a/application/src/main/data/upgrade/3.2.2/schema_update.sql b/application/src/main/data/upgrade/3.2.2/schema_update.sql index e1a2952f64..77a5a9dbbe 100644 --- a/application/src/main/data/upgrade/3.2.2/schema_update.sql +++ b/application/src/main/data/upgrade/3.2.2/schema_update.sql @@ -69,7 +69,7 @@ CREATE TABLE IF NOT EXISTS firmware ( content_type varchar(255), checksum_algorithm varchar(32), checksum varchar(1020), - data bytea, + data oid, data_size bigint, additional_info varchar, search_text varchar(255), diff --git a/application/src/main/java/org/thingsboard/server/service/firmware/DefaultFirmwareStateService.java b/application/src/main/java/org/thingsboard/server/service/firmware/DefaultFirmwareStateService.java index e452626af1..6d358e0ff9 100644 --- a/application/src/main/java/org/thingsboard/server/service/firmware/DefaultFirmwareStateService.java +++ b/application/src/main/java/org/thingsboard/server/service/firmware/DefaultFirmwareStateService.java @@ -22,7 +22,6 @@ import org.thingsboard.rule.engine.api.RuleEngineTelemetryService; import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceProfile; -import org.thingsboard.server.common.data.Firmware; import org.thingsboard.server.common.data.FirmwareInfo; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.FirmwareId; @@ -35,7 +34,6 @@ import org.thingsboard.server.common.data.kv.StringDataEntry; import org.thingsboard.server.common.data.kv.TsKvEntry; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; -import org.thingsboard.server.common.msg.queue.TbCallback; import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; import org.thingsboard.server.dao.device.DeviceProfileService; import org.thingsboard.server.dao.device.DeviceService; @@ -155,7 +153,7 @@ public class DefaultFirmwareStateService implements FirmwareStateService { } if (targetFirmwareId.equals(currentFirmwareId)) { - update(device, firmwareService.findFirmwareById(device.getTenantId(), targetFirmwareId), ts); + update(device, firmwareService.findFirmwareInfoById(device.getTenantId(), targetFirmwareId), ts); isSuccess = true; } else { log.warn("[{}] [{}] Can`t update firmware for the device, target firmwareId: [{}], current firmwareId: [{}]!", tenantId, deviceId, targetFirmwareId, currentFirmwareId); diff --git a/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java b/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java index 058b5090ea..d55ed17357 100644 --- a/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java +++ b/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java @@ -23,11 +23,10 @@ import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.MoreExecutors; import com.google.protobuf.ByteString; import lombok.extern.slf4j.Slf4j; -import org.springframework.cache.CacheManager; import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; import org.thingsboard.common.util.JacksonUtil; -import org.thingsboard.server.cache.firmware.FirmwareCacheWriter; +import org.thingsboard.server.cache.firmware.FirmwareDataCache; import org.thingsboard.server.common.data.ApiUsageState; import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.Device; @@ -61,7 +60,6 @@ import org.thingsboard.server.dao.device.provision.ProvisionRequest; import org.thingsboard.server.dao.device.provision.ProvisionResponse; import org.thingsboard.server.dao.firmware.FirmwareService; import org.thingsboard.server.dao.relation.RelationService; -import org.thingsboard.server.dao.resource.ResourceService; import org.thingsboard.server.dao.tenant.TbTenantProfileCache; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos.DeviceInfoProto; @@ -116,7 +114,7 @@ public class DefaultTransportApiService implements TransportApiService { private final DeviceProvisionService deviceProvisionService; private final TbResourceService resourceService; private final FirmwareService firmwareService; - private final FirmwareCacheWriter firmwareCacheWriter; + private final FirmwareDataCache firmwareDataCache; private final ConcurrentMap deviceCreationLocks = new ConcurrentHashMap<>(); @@ -125,7 +123,7 @@ public class DefaultTransportApiService implements TransportApiService { RelationService relationService, DeviceCredentialsService deviceCredentialsService, DeviceStateService deviceStateService, DbCallbackExecutorService dbCallbackExecutorService, TbClusterService tbClusterService, DataDecodingEncodingService dataDecodingEncodingService, - DeviceProvisionService deviceProvisionService, TbResourceService resourceService, FirmwareService firmwareService, FirmwareCacheWriter firmwareCacheWriter) { + DeviceProvisionService deviceProvisionService, TbResourceService resourceService, FirmwareService firmwareService, FirmwareDataCache firmwareDataCache) { this.deviceProfileCache = deviceProfileCache; this.tenantProfileCache = tenantProfileCache; this.apiUsageStateService = apiUsageStateService; @@ -139,7 +137,7 @@ public class DefaultTransportApiService implements TransportApiService { this.deviceProvisionService = deviceProvisionService; this.resourceService = resourceService; this.firmwareService = firmwareService; - this.firmwareCacheWriter = firmwareCacheWriter; + this.firmwareDataCache = firmwareDataCache; } @Override @@ -485,7 +483,7 @@ public class DefaultTransportApiService implements TransportApiService { builder.setVersion(firmware.getVersion()); builder.setFileName(firmware.getFileName()); builder.setContentType(firmware.getContentType()); - firmwareCacheWriter.put(firmwareId.toString(), firmware.getData().array()); + firmwareDataCache.put(firmwareId.toString(), firmware.getData().array()); } } diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index e8e1fb15f4..69de22fa34 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -372,8 +372,8 @@ caffeine: timeToLiveInMinutes: 20000 maxSize: 10000 firmwares: - timeToLiveInMinutes: 1440 - maxSize: 100 + timeToLiveInMinutes: 60 + maxSize: 10 edges: timeToLiveInMinutes: 1440 maxSize: 0 diff --git a/common/cache/src/main/java/org/thingsboard/server/cache/firmware/AbstractRedisFirmwareCache.java b/common/cache/src/main/java/org/thingsboard/server/cache/firmware/AbstractRedisFirmwareCache.java deleted file mode 100644 index 371b85b94d..0000000000 --- a/common/cache/src/main/java/org/thingsboard/server/cache/firmware/AbstractRedisFirmwareCache.java +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Copyright © 2016-2021 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.cache.firmware; - -import org.springframework.data.redis.connection.RedisConnectionFactory; - -import static org.thingsboard.server.common.data.CacheConstants.FIRMWARE_CACHE; - -public abstract class AbstractRedisFirmwareCache { - - protected final RedisConnectionFactory redisConnectionFactory; - - protected AbstractRedisFirmwareCache(RedisConnectionFactory redisConnectionFactory) { - this.redisConnectionFactory = redisConnectionFactory; - } - - protected byte[] toFirmwareCacheKey(String key) { - return String.format("%s::%s", FIRMWARE_CACHE, key).getBytes(); - } -} diff --git a/common/cache/src/main/java/org/thingsboard/server/cache/firmware/CaffeineFirmwareCacheReader.java b/common/cache/src/main/java/org/thingsboard/server/cache/firmware/CaffeineFirmwareCache.java similarity index 77% rename from common/cache/src/main/java/org/thingsboard/server/cache/firmware/CaffeineFirmwareCacheReader.java rename to common/cache/src/main/java/org/thingsboard/server/cache/firmware/CaffeineFirmwareCache.java index ac53027628..1acb09b28e 100644 --- a/common/cache/src/main/java/org/thingsboard/server/cache/firmware/CaffeineFirmwareCacheReader.java +++ b/common/cache/src/main/java/org/thingsboard/server/cache/firmware/CaffeineFirmwareCache.java @@ -15,22 +15,20 @@ */ package org.thingsboard.server.cache.firmware; -import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; +import lombok.RequiredArgsConstructor; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.cache.CacheManager; import org.springframework.stereotype.Service; import static org.thingsboard.server.common.data.CacheConstants.FIRMWARE_CACHE; @Service -@ConditionalOnExpression("(('${service.type:null}'=='monolith' && '${transport.api_enabled:true}'=='true') || '${service.type:null}'=='tb-transport') && ('${cache.type:null}'=='caffeine' || '${cache.type:null}'=='null')") -public class CaffeineFirmwareCacheReader implements FirmwareCacheReader { +@ConditionalOnProperty(prefix = "cache", value = "type", havingValue = "caffeine", matchIfMissing = true) +@RequiredArgsConstructor +public class CaffeineFirmwareCache implements FirmwareDataCache { private final CacheManager cacheManager; - public CaffeineFirmwareCacheReader(CacheManager cacheManager) { - this.cacheManager = cacheManager; - } - @Override public byte[] get(String key) { return get(key, 0, 0); @@ -57,4 +55,14 @@ public class CaffeineFirmwareCacheReader implements FirmwareCacheReader { } return new byte[0]; } + + @Override + public void put(String key, byte[] value) { + cacheManager.getCache(FIRMWARE_CACHE).putIfAbsent(key, value); + } + + @Override + public void evict(String key) { + cacheManager.getCache(FIRMWARE_CACHE).evict(key); + } } diff --git a/common/cache/src/main/java/org/thingsboard/server/cache/firmware/CaffeineFirmwareCacheWriter.java b/common/cache/src/main/java/org/thingsboard/server/cache/firmware/CaffeineFirmwareCacheWriter.java deleted file mode 100644 index bba4c827bb..0000000000 --- a/common/cache/src/main/java/org/thingsboard/server/cache/firmware/CaffeineFirmwareCacheWriter.java +++ /dev/null @@ -1,38 +0,0 @@ -/** - * Copyright © 2016-2021 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.cache.firmware; - -import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; -import org.springframework.cache.CacheManager; -import org.springframework.stereotype.Service; - -import static org.thingsboard.server.common.data.CacheConstants.FIRMWARE_CACHE; - -@Service -@ConditionalOnExpression("('${service.type:null}'=='monolith' || '${service.type:null}'=='tb-core') && ('${cache.type:null}'=='caffeine' || '${cache.type:null}'=='null')") -public class CaffeineFirmwareCacheWriter implements FirmwareCacheWriter { - - private final CacheManager cacheManager; - - public CaffeineFirmwareCacheWriter(CacheManager cacheManager) { - this.cacheManager = cacheManager; - } - - @Override - public void put(String key, byte[] value) { - cacheManager.getCache(FIRMWARE_CACHE).putIfAbsent(key, value); - } -} diff --git a/common/cache/src/main/java/org/thingsboard/server/cache/firmware/FirmwareCacheWriter.java b/common/cache/src/main/java/org/thingsboard/server/cache/firmware/FirmwareCacheWriter.java deleted file mode 100644 index f1387f9ff1..0000000000 --- a/common/cache/src/main/java/org/thingsboard/server/cache/firmware/FirmwareCacheWriter.java +++ /dev/null @@ -1,20 +0,0 @@ -/** - * Copyright © 2016-2021 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.cache.firmware; - -public interface FirmwareCacheWriter { - void put(String key, byte[] value); -} diff --git a/common/cache/src/main/java/org/thingsboard/server/cache/firmware/FirmwareCacheReader.java b/common/cache/src/main/java/org/thingsboard/server/cache/firmware/FirmwareDataCache.java similarity index 87% rename from common/cache/src/main/java/org/thingsboard/server/cache/firmware/FirmwareCacheReader.java rename to common/cache/src/main/java/org/thingsboard/server/cache/firmware/FirmwareDataCache.java index 4b4dab4caa..2883691459 100644 --- a/common/cache/src/main/java/org/thingsboard/server/cache/firmware/FirmwareCacheReader.java +++ b/common/cache/src/main/java/org/thingsboard/server/cache/firmware/FirmwareDataCache.java @@ -15,8 +15,13 @@ */ package org.thingsboard.server.cache.firmware; -public interface FirmwareCacheReader { +public interface FirmwareDataCache { + byte[] get(String key); byte[] get(String key, int chunkSize, int chunk); + + void put(String key, byte[] value); + + void evict(String key); } diff --git a/common/cache/src/main/java/org/thingsboard/server/cache/firmware/RedisFirmwareCacheWriter.java b/common/cache/src/main/java/org/thingsboard/server/cache/firmware/RedisFirmwareCacheWriter.java deleted file mode 100644 index 7bbb6045e9..0000000000 --- a/common/cache/src/main/java/org/thingsboard/server/cache/firmware/RedisFirmwareCacheWriter.java +++ /dev/null @@ -1,38 +0,0 @@ -/** - * Copyright © 2016-2021 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.cache.firmware; - -import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; -import org.springframework.data.redis.connection.RedisConnection; -import org.springframework.data.redis.connection.RedisConnectionFactory; -import org.springframework.stereotype.Service; - -@Service -@ConditionalOnExpression("('${service.type:null}'=='monolith' || '${service.type:null}'=='tb-core') && '${cache.type:null}'=='redis'") -public class RedisFirmwareCacheWriter extends AbstractRedisFirmwareCache implements FirmwareCacheWriter { - - public RedisFirmwareCacheWriter(RedisConnectionFactory redisConnectionFactory) { - super(redisConnectionFactory); - } - - @Override - public void put(String key, byte[] value) { - try (RedisConnection connection = redisConnectionFactory.getConnection()) { - connection.set(toFirmwareCacheKey(key), value); - } - } - -} diff --git a/common/cache/src/main/java/org/thingsboard/server/cache/firmware/RedisFirmwareCacheReader.java b/common/cache/src/main/java/org/thingsboard/server/cache/firmware/RedisFirmwareDataCache.java similarity index 61% rename from common/cache/src/main/java/org/thingsboard/server/cache/firmware/RedisFirmwareCacheReader.java rename to common/cache/src/main/java/org/thingsboard/server/cache/firmware/RedisFirmwareDataCache.java index 9bf8b40230..08dd6facc2 100644 --- a/common/cache/src/main/java/org/thingsboard/server/cache/firmware/RedisFirmwareCacheReader.java +++ b/common/cache/src/main/java/org/thingsboard/server/cache/firmware/RedisFirmwareDataCache.java @@ -15,18 +15,20 @@ */ package org.thingsboard.server.cache.firmware; -import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; +import lombok.RequiredArgsConstructor; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.data.redis.connection.RedisConnection; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.stereotype.Service; -@Service -@ConditionalOnExpression("(('${service.type:null}'=='monolith' && '${transport.api_enabled:true}'=='true') || '${service.type:null}'=='tb-transport') && '${cache.type:null}'=='redis'") -public class RedisFirmwareCacheReader extends AbstractRedisFirmwareCache implements FirmwareCacheReader { +import static org.thingsboard.server.common.data.CacheConstants.FIRMWARE_CACHE; - public RedisFirmwareCacheReader(RedisConnectionFactory redisConnectionFactory) { - super(redisConnectionFactory); - } +@Service +@ConditionalOnProperty(prefix = "cache", value = "type", havingValue = "redis") +@RequiredArgsConstructor +public class RedisFirmwareDataCache implements FirmwareDataCache { + + private final RedisConnectionFactory redisConnectionFactory; @Override public byte[] get(String key) { @@ -46,4 +48,21 @@ public class RedisFirmwareCacheReader extends AbstractRedisFirmwareCache impleme } } + @Override + public void put(String key, byte[] value) { + try (RedisConnection connection = redisConnectionFactory.getConnection()) { + connection.set(toFirmwareCacheKey(key), value); + } + } + + @Override + public void evict(String key) { + try (RedisConnection connection = redisConnectionFactory.getConnection()) { + connection.del(toFirmwareCacheKey(key)); + } + } + + private byte[] toFirmwareCacheKey(String key) { + return String.format("%s::%s", FIRMWARE_CACHE, key).getBytes(); + } } diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/session/FeatureType.java b/common/message/src/main/java/org/thingsboard/server/common/msg/session/FeatureType.java index 9be2957401..2756276dc4 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/session/FeatureType.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/session/FeatureType.java @@ -16,5 +16,5 @@ package org.thingsboard.server.common.msg.session; public enum FeatureType { - ATTRIBUTES, TELEMETRY, RPC, CLAIM, PROVISION + ATTRIBUTES, TELEMETRY, RPC, CLAIM, PROVISION, FIRMWARE } diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/session/SessionMsgType.java b/common/message/src/main/java/org/thingsboard/server/common/msg/session/SessionMsgType.java index 5dbea04d59..a6f05018f8 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/session/SessionMsgType.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/session/SessionMsgType.java @@ -30,7 +30,9 @@ public enum SessionMsgType { SESSION_OPEN, SESSION_CLOSE, - CLAIM_REQUEST(); + CLAIM_REQUEST(), + + GET_FIRMWARE_REQUEST; private final boolean requiresRulesProcessing; diff --git a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java index aa4236ccf1..78ea5f3df0 100644 --- a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java +++ b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java @@ -20,6 +20,7 @@ import com.google.protobuf.Descriptors; import com.google.protobuf.DynamicMessage; import lombok.Data; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; import org.eclipse.californium.core.coap.CoAP; import org.eclipse.californium.core.coap.Request; import org.eclipse.californium.core.coap.Response; @@ -28,7 +29,6 @@ import org.eclipse.californium.core.observe.ObserveRelation; import org.eclipse.californium.core.server.resources.CoapExchange; import org.eclipse.californium.core.server.resources.Resource; import org.eclipse.californium.core.server.resources.ResourceObserver; -import org.springframework.util.StringUtils; import org.thingsboard.server.coapserver.CoapServerService; import org.thingsboard.server.coapserver.TbCoapDtlsSessionInfo; import org.thingsboard.server.common.data.DataConstants; @@ -120,6 +120,8 @@ public class CoapTransportResource extends AbstractCoapTransportResource { processExchangeGetRequest(exchange, featureType.get()); } else if (featureType.get() == FeatureType.ATTRIBUTES) { processRequest(exchange, SessionMsgType.GET_ATTRIBUTES_REQUEST); + } else if (featureType.get() == FeatureType.FIRMWARE) { + processRequest(exchange, SessionMsgType.GET_FIRMWARE_REQUEST); } else { log.trace("Invalid feature type parameter"); exchange.respond(CoAP.ResponseCode.BAD_REQUEST); @@ -201,7 +203,7 @@ public class CoapTransportResource extends AbstractCoapTransportResource { Request request = advanced.getRequest(); String dtlsSessionIdStr = request.getSourceContext().get(DTLS_SESSION_ID_KEY); - if (!StringUtils.isEmpty(dtlsSessionIdStr)) { + if (StringUtils.isNotEmpty(dtlsSessionIdStr)) { if (dtlsSessionIdMap != null) { TbCoapDtlsSessionInfo tbCoapDtlsSessionInfo = dtlsSessionIdMap .computeIfPresent(dtlsSessionIdStr, (dtlsSessionId, dtlsSessionInfo) -> { @@ -323,6 +325,14 @@ public class CoapTransportResource extends AbstractCoapTransportResource { coapTransportAdaptor.convertToGetAttributes(sessionId, request), new CoapNoOpCallback(exchange)); break; + case GET_FIRMWARE_REQUEST: + TransportProtos.GetFirmwareRequestMsg requestMsg = TransportProtos.GetFirmwareRequestMsg.newBuilder() + .setTenantIdMSB(sessionInfo.getTenantIdMSB()) + .setTenantIdLSB(sessionInfo.getTenantIdLSB()) + .setDeviceIdMSB(sessionInfo.getDeviceIdMSB()) + .setDeviceIdLSB(sessionInfo.getDeviceIdLSB()).build(); + transportContext.getTransportService().process(sessionInfo, requestMsg, new FirmwareCallback(exchange)); + break; } } catch (AdaptorException e) { log.trace("[{}] Failed to decode message: ", sessionId, e); @@ -424,6 +434,40 @@ public class CoapTransportResource extends AbstractCoapTransportResource { } } + private class FirmwareCallback implements TransportServiceCallback { + private final CoapExchange exchange; + + FirmwareCallback(CoapExchange exchange) { + this.exchange = exchange; + } + + @Override + public void onSuccess(TransportProtos.GetFirmwareResponseMsg msg) { + String title = exchange.getQueryParameter("title"); + String version = exchange.getQueryParameter("version"); + if (msg.getResponseStatus().equals(TransportProtos.ResponseStatus.SUCCESS)) { + if (msg.getTitle().equals(title) && msg.getVersion().equals(version)) { + String firmwareId = new UUID(msg.getFirmwareIdMSB(), msg.getFirmwareIdLSB()).toString(); + String strChunkSize = exchange.getQueryParameter("chunkSize"); + String strChunk = exchange.getQueryParameter("chunk"); + int chunkSize = StringUtils.isEmpty(strChunkSize) ? 0 : Integer.parseInt(strChunkSize); + int chunk = StringUtils.isEmpty(strChunk) ? 0 : Integer.parseInt(strChunk); + exchange.respond(CoAP.ResponseCode.CONTENT, transportContext.getFirmwareDataCache().get(firmwareId, chunkSize, chunk)); + } else { + exchange.respond(CoAP.ResponseCode.BAD_REQUEST); + } + } else { + exchange.respond(CoAP.ResponseCode.NOT_FOUND); + } + } + + @Override + public void onError(Throwable e) { + log.warn("Failed to process request", e); + exchange.respond(CoAP.ResponseCode.INTERNAL_SERVER_ERROR); + } + } + private static class CoapSessionListener implements SessionMsgListener { private final CoapExchange exchange; diff --git a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportService.java b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportService.java index af4cb021e1..a165cde74b 100644 --- a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportService.java +++ b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportService.java @@ -18,7 +18,6 @@ package org.thingsboard.server.transport.coap; import lombok.extern.slf4j.Slf4j; import org.eclipse.californium.core.CoapResource; import org.eclipse.californium.core.CoapServer; - import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.stereotype.Service; diff --git a/common/transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java b/common/transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java index dc3b980f88..62b46a5fe7 100644 --- a/common/transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java +++ b/common/transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java @@ -299,7 +299,7 @@ public class DeviceApiController { responseWriter.setResult(new ResponseEntity<>(HttpStatus.NOT_FOUND)); } else if (title.equals(firmwareResponseMsg.getTitle()) && version.equals(firmwareResponseMsg.getVersion())) { String firmwareId = new UUID(firmwareResponseMsg.getFirmwareIdMSB(), firmwareResponseMsg.getFirmwareIdLSB()).toString(); - ByteArrayResource resource = new ByteArrayResource(transportContext.getFirmwareCacheReader().get(firmwareId, chuckSize, chuck)); + ByteArrayResource resource = new ByteArrayResource(transportContext.getFirmwareDataCache().get(firmwareId, chuckSize, chuck)); ResponseEntity response = ResponseEntity.ok() .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + firmwareResponseMsg.getFileName()) .header("x-filename", firmwareResponseMsg.getFileName()) diff --git a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java index 73ca2cdf9d..5e4d8a75b9 100644 --- a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java +++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java @@ -452,7 +452,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement log.trace("[{}] Send firmware [{}] to device!", sessionId, firmwareId); ack(ctx, msgId); try { - byte[] firmwareChunk = context.getFirmwareCacheReader().get(firmwareId, chunkSize, chunk); + byte[] firmwareChunk = context.getFirmwareDataCache().get(firmwareId, chunkSize, chunk); deviceSessionCtx.getPayloadAdaptor() .convertToPublish(deviceSessionCtx, firmwareChunk, requestId, chunk) .ifPresent(deviceSessionCtx.getChannel()::writeAndFlush); diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportContext.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportContext.java index a1d09b78a7..6273c1f155 100644 --- a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportContext.java +++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportContext.java @@ -20,7 +20,7 @@ import lombok.Data; import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; -import org.thingsboard.server.cache.firmware.FirmwareCacheReader; +import org.thingsboard.server.cache.firmware.FirmwareDataCache; import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; import org.thingsboard.server.queue.scheduler.SchedulerComponent; @@ -53,7 +53,7 @@ public abstract class TransportContext { @Getter @Autowired - private FirmwareCacheReader firmwareCacheReader; + private FirmwareDataCache firmwareDataCache; @PostConstruct public void init() { diff --git a/dao/src/main/java/org/thingsboard/server/dao/firmware/BaseFirmwareService.java b/dao/src/main/java/org/thingsboard/server/dao/firmware/BaseFirmwareService.java index d504845488..5876947440 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/firmware/BaseFirmwareService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/firmware/BaseFirmwareService.java @@ -18,12 +18,15 @@ package org.thingsboard.server.dao.firmware; import com.google.common.hash.HashFunction; import com.google.common.hash.Hashing; import com.google.common.util.concurrent.ListenableFuture; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.hibernate.exception.ConstraintViolationException; import org.springframework.cache.Cache; import org.springframework.cache.CacheManager; +import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; +import org.thingsboard.server.cache.firmware.FirmwareDataCache; import org.thingsboard.server.common.data.Firmware; import org.thingsboard.server.common.data.FirmwareInfo; import org.thingsboard.server.common.data.Tenant; @@ -38,6 +41,7 @@ import org.thingsboard.server.dao.tenant.TenantDao; import java.nio.ByteBuffer; import java.util.Collections; +import java.util.List; import java.util.Optional; import static org.thingsboard.server.common.data.CacheConstants.FIRMWARE_CACHE; @@ -46,6 +50,7 @@ import static org.thingsboard.server.dao.service.Validator.validatePageLink; @Service @Slf4j +@RequiredArgsConstructor public class BaseFirmwareService implements FirmwareService { public static final String INCORRECT_FIRMWARE_ID = "Incorrect firmwareId "; public static final String INCORRECT_TENANT_ID = "Incorrect tenantId "; @@ -54,13 +59,7 @@ public class BaseFirmwareService implements FirmwareService { private final FirmwareDao firmwareDao; private final FirmwareInfoDao firmwareInfoDao; private final CacheManager cacheManager; - - public BaseFirmwareService(TenantDao tenantDao, FirmwareDao firmwareDao, FirmwareInfoDao firmwareInfoDao, CacheManager cacheManager) { - this.tenantDao = tenantDao; - this.firmwareDao = firmwareDao; - this.firmwareInfoDao = firmwareInfoDao; - this.cacheManager = cacheManager; - } + private final FirmwareDataCache firmwareDataCache; @Override public FirmwareInfo saveFirmwareInfo(FirmwareInfo firmwareInfo) { @@ -69,7 +68,9 @@ public class BaseFirmwareService implements FirmwareService { try { FirmwareId firmwareId = firmwareInfo.getId(); if (firmwareId != null) { - cacheManager.getCache(FIRMWARE_CACHE).evict(firmwareId.toString()); + Cache cache = cacheManager.getCache(FIRMWARE_CACHE); + cache.evict(toFirmwareInfoKey(firmwareId)); + firmwareDataCache.evict(firmwareId.toString()); } return firmwareInfoDao.save(firmwareInfo.getTenantId(), firmwareInfo); } catch (Exception t) { @@ -89,7 +90,9 @@ public class BaseFirmwareService implements FirmwareService { try { FirmwareId firmwareId = firmware.getId(); if (firmwareId != null) { - cacheManager.getCache(FIRMWARE_CACHE).evict(firmwareId.toString()); + Cache cache = cacheManager.getCache(FIRMWARE_CACHE); + cache.evict(toFirmwareInfoKey(firmwareId)); + firmwareDataCache.evict(firmwareId.toString()); } return firmwareDao.save(firmware.getTenantId(), firmware); } catch (Exception t) { @@ -110,6 +113,7 @@ public class BaseFirmwareService implements FirmwareService { } @Override + @Cacheable(cacheNames = FIRMWARE_CACHE, key = "{#firmwareId}") public FirmwareInfo findFirmwareInfoById(TenantId tenantId, FirmwareId firmwareId) { log.trace("Executing findFirmwareInfoById [{}]", firmwareId); validateId(firmwareId, INCORRECT_FIRMWARE_ID + firmwareId); @@ -144,7 +148,8 @@ public class BaseFirmwareService implements FirmwareService { validateId(firmwareId, INCORRECT_FIRMWARE_ID + firmwareId); try { Cache cache = cacheManager.getCache(FIRMWARE_CACHE); - cache.evict(Collections.singletonList(firmwareId)); + cache.evict(toFirmwareInfoKey(firmwareId)); + firmwareDataCache.evict(firmwareId.toString()); firmwareDao.removeById(tenantId, firmwareId.getId()); } catch (Exception t) { ConstraintViolationException e = extractConstraintViolationException(t).orElse(null); @@ -323,4 +328,9 @@ public class BaseFirmwareService implements FirmwareService { return Optional.empty(); } } + + private static List toFirmwareInfoKey(FirmwareId firmwareId) { + return Collections.singletonList(firmwareId); + } + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/FirmwareEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/FirmwareEntity.java index ccfa0229bd..690bb4e6d9 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/FirmwareEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/FirmwareEntity.java @@ -30,6 +30,7 @@ import org.thingsboard.server.dao.util.mapping.JsonStringType; import javax.persistence.Column; import javax.persistence.Entity; +import javax.persistence.Lob; import javax.persistence.Table; import java.nio.ByteBuffer; import java.util.UUID; @@ -74,6 +75,7 @@ public class FirmwareEntity extends BaseSqlEntity implements SearchTex @Column(name = FIRMWARE_CHECKSUM_COLUMN) private String checksum; + @Lob @Column(name = FIRMWARE_DATA_COLUMN, columnDefinition = "BINARY") private byte[] data; diff --git a/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java index 6b0ae89670..69fc94141d 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java @@ -35,6 +35,7 @@ import org.thingsboard.server.dao.device.DeviceService; import org.thingsboard.server.dao.entity.AbstractEntityService; import org.thingsboard.server.dao.entityview.EntityViewService; import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.dao.firmware.FirmwareService; import org.thingsboard.server.dao.resource.ResourceService; import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.dao.service.DataValidator; @@ -92,6 +93,9 @@ public class TenantServiceImpl extends AbstractEntityService implements TenantSe @Autowired private ResourceService resourceService; + @Autowired + private FirmwareService firmwareService; + @Override public Tenant findTenantById(TenantId tenantId) { log.trace("Executing findTenantById [{}]", tenantId); @@ -146,6 +150,7 @@ public class TenantServiceImpl extends AbstractEntityService implements TenantSe ruleChainService.deleteRuleChainsByTenantId(tenantId); apiUsageStateService.deleteApiUsageStateByTenantId(tenantId); resourceService.deleteResourcesByTenantId(tenantId); + firmwareService.deleteFirmwaresByTenantId(tenantId); tenantDao.removeById(tenantId, tenantId.getId()); deleteEntityRelations(tenantId, tenantId); } diff --git a/dao/src/main/resources/sql/schema-entities.sql b/dao/src/main/resources/sql/schema-entities.sql index 7d7a18abc6..f71401ed57 100644 --- a/dao/src/main/resources/sql/schema-entities.sql +++ b/dao/src/main/resources/sql/schema-entities.sql @@ -186,7 +186,7 @@ CREATE TABLE IF NOT EXISTS firmware ( content_type varchar(255), checksum_algorithm varchar(32), checksum varchar(1020), - data bytea, + data oid, data_size bigint, additional_info varchar, search_text varchar(255),