CoAP support for firmware updates

This commit is contained in:
Andrii Shvaika 2021-04-28 13:03:24 +03:00
commit edc993427e
22 changed files with 138 additions and 177 deletions

View File

@ -69,7 +69,7 @@ CREATE TABLE IF NOT EXISTS firmware (
content_type varchar(255), content_type varchar(255),
checksum_algorithm varchar(32), checksum_algorithm varchar(32),
checksum varchar(1020), checksum varchar(1020),
data bytea, data oid,
data_size bigint, data_size bigint,
additional_info varchar, additional_info varchar,
search_text varchar(255), search_text varchar(255),

View File

@ -22,7 +22,6 @@ import org.thingsboard.rule.engine.api.RuleEngineTelemetryService;
import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceProfile; 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.FirmwareInfo;
import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.FirmwareId; 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.kv.TsKvEntry;
import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink; 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.common.msg.queue.TopicPartitionInfo;
import org.thingsboard.server.dao.device.DeviceProfileService; import org.thingsboard.server.dao.device.DeviceProfileService;
import org.thingsboard.server.dao.device.DeviceService; import org.thingsboard.server.dao.device.DeviceService;
@ -155,7 +153,7 @@ public class DefaultFirmwareStateService implements FirmwareStateService {
} }
if (targetFirmwareId.equals(currentFirmwareId)) { if (targetFirmwareId.equals(currentFirmwareId)) {
update(device, firmwareService.findFirmwareById(device.getTenantId(), targetFirmwareId), ts); update(device, firmwareService.findFirmwareInfoById(device.getTenantId(), targetFirmwareId), ts);
isSuccess = true; isSuccess = true;
} else { } else {
log.warn("[{}] [{}] Can`t update firmware for the device, target firmwareId: [{}], current firmwareId: [{}]!", tenantId, deviceId, targetFirmwareId, currentFirmwareId); log.warn("[{}] [{}] Can`t update firmware for the device, target firmwareId: [{}], current firmwareId: [{}]!", tenantId, deviceId, targetFirmwareId, currentFirmwareId);

View File

@ -23,11 +23,10 @@ import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors; import com.google.common.util.concurrent.MoreExecutors;
import com.google.protobuf.ByteString; import com.google.protobuf.ByteString;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.CacheManager;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import org.thingsboard.common.util.JacksonUtil; 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.ApiUsageState;
import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.Device; 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.device.provision.ProvisionResponse;
import org.thingsboard.server.dao.firmware.FirmwareService; import org.thingsboard.server.dao.firmware.FirmwareService;
import org.thingsboard.server.dao.relation.RelationService; 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.dao.tenant.TbTenantProfileCache;
import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.gen.transport.TransportProtos.DeviceInfoProto; import org.thingsboard.server.gen.transport.TransportProtos.DeviceInfoProto;
@ -116,7 +114,7 @@ public class DefaultTransportApiService implements TransportApiService {
private final DeviceProvisionService deviceProvisionService; private final DeviceProvisionService deviceProvisionService;
private final TbResourceService resourceService; private final TbResourceService resourceService;
private final FirmwareService firmwareService; private final FirmwareService firmwareService;
private final FirmwareCacheWriter firmwareCacheWriter; private final FirmwareDataCache firmwareDataCache;
private final ConcurrentMap<String, ReentrantLock> deviceCreationLocks = new ConcurrentHashMap<>(); private final ConcurrentMap<String, ReentrantLock> deviceCreationLocks = new ConcurrentHashMap<>();
@ -125,7 +123,7 @@ public class DefaultTransportApiService implements TransportApiService {
RelationService relationService, DeviceCredentialsService deviceCredentialsService, RelationService relationService, DeviceCredentialsService deviceCredentialsService,
DeviceStateService deviceStateService, DbCallbackExecutorService dbCallbackExecutorService, DeviceStateService deviceStateService, DbCallbackExecutorService dbCallbackExecutorService,
TbClusterService tbClusterService, DataDecodingEncodingService dataDecodingEncodingService, TbClusterService tbClusterService, DataDecodingEncodingService dataDecodingEncodingService,
DeviceProvisionService deviceProvisionService, TbResourceService resourceService, FirmwareService firmwareService, FirmwareCacheWriter firmwareCacheWriter) { DeviceProvisionService deviceProvisionService, TbResourceService resourceService, FirmwareService firmwareService, FirmwareDataCache firmwareDataCache) {
this.deviceProfileCache = deviceProfileCache; this.deviceProfileCache = deviceProfileCache;
this.tenantProfileCache = tenantProfileCache; this.tenantProfileCache = tenantProfileCache;
this.apiUsageStateService = apiUsageStateService; this.apiUsageStateService = apiUsageStateService;
@ -139,7 +137,7 @@ public class DefaultTransportApiService implements TransportApiService {
this.deviceProvisionService = deviceProvisionService; this.deviceProvisionService = deviceProvisionService;
this.resourceService = resourceService; this.resourceService = resourceService;
this.firmwareService = firmwareService; this.firmwareService = firmwareService;
this.firmwareCacheWriter = firmwareCacheWriter; this.firmwareDataCache = firmwareDataCache;
} }
@Override @Override
@ -485,7 +483,7 @@ public class DefaultTransportApiService implements TransportApiService {
builder.setVersion(firmware.getVersion()); builder.setVersion(firmware.getVersion());
builder.setFileName(firmware.getFileName()); builder.setFileName(firmware.getFileName());
builder.setContentType(firmware.getContentType()); builder.setContentType(firmware.getContentType());
firmwareCacheWriter.put(firmwareId.toString(), firmware.getData().array()); firmwareDataCache.put(firmwareId.toString(), firmware.getData().array());
} }
} }

View File

@ -372,8 +372,8 @@ caffeine:
timeToLiveInMinutes: 20000 timeToLiveInMinutes: 20000
maxSize: 10000 maxSize: 10000
firmwares: firmwares:
timeToLiveInMinutes: 1440 timeToLiveInMinutes: 60
maxSize: 100 maxSize: 10
edges: edges:
timeToLiveInMinutes: 1440 timeToLiveInMinutes: 1440
maxSize: 0 maxSize: 0

View File

@ -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();
}
}

View File

@ -15,22 +15,20 @@
*/ */
package org.thingsboard.server.cache.firmware; 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.cache.CacheManager;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import static org.thingsboard.server.common.data.CacheConstants.FIRMWARE_CACHE; import static org.thingsboard.server.common.data.CacheConstants.FIRMWARE_CACHE;
@Service @Service
@ConditionalOnExpression("(('${service.type:null}'=='monolith' && '${transport.api_enabled:true}'=='true') || '${service.type:null}'=='tb-transport') && ('${cache.type:null}'=='caffeine' || '${cache.type:null}'=='null')") @ConditionalOnProperty(prefix = "cache", value = "type", havingValue = "caffeine", matchIfMissing = true)
public class CaffeineFirmwareCacheReader implements FirmwareCacheReader { @RequiredArgsConstructor
public class CaffeineFirmwareCache implements FirmwareDataCache {
private final CacheManager cacheManager; private final CacheManager cacheManager;
public CaffeineFirmwareCacheReader(CacheManager cacheManager) {
this.cacheManager = cacheManager;
}
@Override @Override
public byte[] get(String key) { public byte[] get(String key) {
return get(key, 0, 0); return get(key, 0, 0);
@ -57,4 +55,14 @@ public class CaffeineFirmwareCacheReader implements FirmwareCacheReader {
} }
return new byte[0]; 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);
}
} }

View File

@ -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);
}
}

View File

@ -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);
}

View File

@ -15,8 +15,13 @@
*/ */
package org.thingsboard.server.cache.firmware; package org.thingsboard.server.cache.firmware;
public interface FirmwareCacheReader { public interface FirmwareDataCache {
byte[] get(String key); byte[] get(String key);
byte[] get(String key, int chunkSize, int chunk); byte[] get(String key, int chunkSize, int chunk);
void put(String key, byte[] value);
void evict(String key);
} }

View File

@ -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);
}
}
}

View File

@ -15,18 +15,20 @@
*/ */
package org.thingsboard.server.cache.firmware; 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.RedisConnection;
import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@Service import static org.thingsboard.server.common.data.CacheConstants.FIRMWARE_CACHE;
@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 {
public RedisFirmwareCacheReader(RedisConnectionFactory redisConnectionFactory) { @Service
super(redisConnectionFactory); @ConditionalOnProperty(prefix = "cache", value = "type", havingValue = "redis")
} @RequiredArgsConstructor
public class RedisFirmwareDataCache implements FirmwareDataCache {
private final RedisConnectionFactory redisConnectionFactory;
@Override @Override
public byte[] get(String key) { 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();
}
} }

View File

@ -16,5 +16,5 @@
package org.thingsboard.server.common.msg.session; package org.thingsboard.server.common.msg.session;
public enum FeatureType { public enum FeatureType {
ATTRIBUTES, TELEMETRY, RPC, CLAIM, PROVISION ATTRIBUTES, TELEMETRY, RPC, CLAIM, PROVISION, FIRMWARE
} }

View File

@ -30,7 +30,9 @@ public enum SessionMsgType {
SESSION_OPEN, SESSION_CLOSE, SESSION_OPEN, SESSION_CLOSE,
CLAIM_REQUEST(); CLAIM_REQUEST(),
GET_FIRMWARE_REQUEST;
private final boolean requiresRulesProcessing; private final boolean requiresRulesProcessing;

View File

@ -20,6 +20,7 @@ import com.google.protobuf.Descriptors;
import com.google.protobuf.DynamicMessage; import com.google.protobuf.DynamicMessage;
import lombok.Data; import lombok.Data;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.californium.core.coap.CoAP; import org.eclipse.californium.core.coap.CoAP;
import org.eclipse.californium.core.coap.Request; import org.eclipse.californium.core.coap.Request;
import org.eclipse.californium.core.coap.Response; 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.CoapExchange;
import org.eclipse.californium.core.server.resources.Resource; import org.eclipse.californium.core.server.resources.Resource;
import org.eclipse.californium.core.server.resources.ResourceObserver; import org.eclipse.californium.core.server.resources.ResourceObserver;
import org.springframework.util.StringUtils;
import org.thingsboard.server.coapserver.CoapServerService; import org.thingsboard.server.coapserver.CoapServerService;
import org.thingsboard.server.coapserver.TbCoapDtlsSessionInfo; import org.thingsboard.server.coapserver.TbCoapDtlsSessionInfo;
import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.DataConstants;
@ -120,6 +120,8 @@ public class CoapTransportResource extends AbstractCoapTransportResource {
processExchangeGetRequest(exchange, featureType.get()); processExchangeGetRequest(exchange, featureType.get());
} else if (featureType.get() == FeatureType.ATTRIBUTES) { } else if (featureType.get() == FeatureType.ATTRIBUTES) {
processRequest(exchange, SessionMsgType.GET_ATTRIBUTES_REQUEST); processRequest(exchange, SessionMsgType.GET_ATTRIBUTES_REQUEST);
} else if (featureType.get() == FeatureType.FIRMWARE) {
processRequest(exchange, SessionMsgType.GET_FIRMWARE_REQUEST);
} else { } else {
log.trace("Invalid feature type parameter"); log.trace("Invalid feature type parameter");
exchange.respond(CoAP.ResponseCode.BAD_REQUEST); exchange.respond(CoAP.ResponseCode.BAD_REQUEST);
@ -201,7 +203,7 @@ public class CoapTransportResource extends AbstractCoapTransportResource {
Request request = advanced.getRequest(); Request request = advanced.getRequest();
String dtlsSessionIdStr = request.getSourceContext().get(DTLS_SESSION_ID_KEY); String dtlsSessionIdStr = request.getSourceContext().get(DTLS_SESSION_ID_KEY);
if (!StringUtils.isEmpty(dtlsSessionIdStr)) { if (StringUtils.isNotEmpty(dtlsSessionIdStr)) {
if (dtlsSessionIdMap != null) { if (dtlsSessionIdMap != null) {
TbCoapDtlsSessionInfo tbCoapDtlsSessionInfo = dtlsSessionIdMap TbCoapDtlsSessionInfo tbCoapDtlsSessionInfo = dtlsSessionIdMap
.computeIfPresent(dtlsSessionIdStr, (dtlsSessionId, dtlsSessionInfo) -> { .computeIfPresent(dtlsSessionIdStr, (dtlsSessionId, dtlsSessionInfo) -> {
@ -323,6 +325,14 @@ public class CoapTransportResource extends AbstractCoapTransportResource {
coapTransportAdaptor.convertToGetAttributes(sessionId, request), coapTransportAdaptor.convertToGetAttributes(sessionId, request),
new CoapNoOpCallback(exchange)); new CoapNoOpCallback(exchange));
break; 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) { } catch (AdaptorException e) {
log.trace("[{}] Failed to decode message: ", sessionId, e); log.trace("[{}] Failed to decode message: ", sessionId, e);
@ -424,6 +434,40 @@ public class CoapTransportResource extends AbstractCoapTransportResource {
} }
} }
private class FirmwareCallback implements TransportServiceCallback<TransportProtos.GetFirmwareResponseMsg> {
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 static class CoapSessionListener implements SessionMsgListener {
private final CoapExchange exchange; private final CoapExchange exchange;

View File

@ -18,7 +18,6 @@ package org.thingsboard.server.transport.coap;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.eclipse.californium.core.CoapResource; import org.eclipse.californium.core.CoapResource;
import org.eclipse.californium.core.CoapServer; import org.eclipse.californium.core.CoapServer;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;

View File

@ -299,7 +299,7 @@ public class DeviceApiController {
responseWriter.setResult(new ResponseEntity<>(HttpStatus.NOT_FOUND)); responseWriter.setResult(new ResponseEntity<>(HttpStatus.NOT_FOUND));
} else if (title.equals(firmwareResponseMsg.getTitle()) && version.equals(firmwareResponseMsg.getVersion())) { } else if (title.equals(firmwareResponseMsg.getTitle()) && version.equals(firmwareResponseMsg.getVersion())) {
String firmwareId = new UUID(firmwareResponseMsg.getFirmwareIdMSB(), firmwareResponseMsg.getFirmwareIdLSB()).toString(); 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<ByteArrayResource> response = ResponseEntity.ok() ResponseEntity<ByteArrayResource> response = ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + firmwareResponseMsg.getFileName()) .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + firmwareResponseMsg.getFileName())
.header("x-filename", firmwareResponseMsg.getFileName()) .header("x-filename", firmwareResponseMsg.getFileName())

View File

@ -452,7 +452,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
log.trace("[{}] Send firmware [{}] to device!", sessionId, firmwareId); log.trace("[{}] Send firmware [{}] to device!", sessionId, firmwareId);
ack(ctx, msgId); ack(ctx, msgId);
try { try {
byte[] firmwareChunk = context.getFirmwareCacheReader().get(firmwareId, chunkSize, chunk); byte[] firmwareChunk = context.getFirmwareDataCache().get(firmwareId, chunkSize, chunk);
deviceSessionCtx.getPayloadAdaptor() deviceSessionCtx.getPayloadAdaptor()
.convertToPublish(deviceSessionCtx, firmwareChunk, requestId, chunk) .convertToPublish(deviceSessionCtx, firmwareChunk, requestId, chunk)
.ifPresent(deviceSessionCtx.getChannel()::writeAndFlush); .ifPresent(deviceSessionCtx.getChannel()::writeAndFlush);

View File

@ -20,7 +20,7 @@ import lombok.Data;
import lombok.Getter; import lombok.Getter;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.thingsboard.server.cache.firmware.FirmwareCacheReader; import org.thingsboard.server.cache.firmware.FirmwareDataCache;
import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; import org.thingsboard.server.queue.discovery.TbServiceInfoProvider;
import org.thingsboard.server.queue.scheduler.SchedulerComponent; import org.thingsboard.server.queue.scheduler.SchedulerComponent;
@ -53,7 +53,7 @@ public abstract class TransportContext {
@Getter @Getter
@Autowired @Autowired
private FirmwareCacheReader firmwareCacheReader; private FirmwareDataCache firmwareDataCache;
@PostConstruct @PostConstruct
public void init() { public void init() {

View File

@ -18,12 +18,15 @@ package org.thingsboard.server.dao.firmware;
import com.google.common.hash.HashFunction; import com.google.common.hash.HashFunction;
import com.google.common.hash.Hashing; import com.google.common.hash.Hashing;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.hibernate.exception.ConstraintViolationException; import org.hibernate.exception.ConstraintViolationException;
import org.springframework.cache.Cache; import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager; import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service; 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.Firmware;
import org.thingsboard.server.common.data.FirmwareInfo; import org.thingsboard.server.common.data.FirmwareInfo;
import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.Tenant;
@ -38,6 +41,7 @@ import org.thingsboard.server.dao.tenant.TenantDao;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.Collections; import java.util.Collections;
import java.util.List;
import java.util.Optional; import java.util.Optional;
import static org.thingsboard.server.common.data.CacheConstants.FIRMWARE_CACHE; import static org.thingsboard.server.common.data.CacheConstants.FIRMWARE_CACHE;
@ -46,6 +50,7 @@ import static org.thingsboard.server.dao.service.Validator.validatePageLink;
@Service @Service
@Slf4j @Slf4j
@RequiredArgsConstructor
public class BaseFirmwareService implements FirmwareService { public class BaseFirmwareService implements FirmwareService {
public static final String INCORRECT_FIRMWARE_ID = "Incorrect firmwareId "; public static final String INCORRECT_FIRMWARE_ID = "Incorrect firmwareId ";
public static final String INCORRECT_TENANT_ID = "Incorrect tenantId "; public static final String INCORRECT_TENANT_ID = "Incorrect tenantId ";
@ -54,13 +59,7 @@ public class BaseFirmwareService implements FirmwareService {
private final FirmwareDao firmwareDao; private final FirmwareDao firmwareDao;
private final FirmwareInfoDao firmwareInfoDao; private final FirmwareInfoDao firmwareInfoDao;
private final CacheManager cacheManager; private final CacheManager cacheManager;
private final FirmwareDataCache firmwareDataCache;
public BaseFirmwareService(TenantDao tenantDao, FirmwareDao firmwareDao, FirmwareInfoDao firmwareInfoDao, CacheManager cacheManager) {
this.tenantDao = tenantDao;
this.firmwareDao = firmwareDao;
this.firmwareInfoDao = firmwareInfoDao;
this.cacheManager = cacheManager;
}
@Override @Override
public FirmwareInfo saveFirmwareInfo(FirmwareInfo firmwareInfo) { public FirmwareInfo saveFirmwareInfo(FirmwareInfo firmwareInfo) {
@ -69,7 +68,9 @@ public class BaseFirmwareService implements FirmwareService {
try { try {
FirmwareId firmwareId = firmwareInfo.getId(); FirmwareId firmwareId = firmwareInfo.getId();
if (firmwareId != null) { 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); return firmwareInfoDao.save(firmwareInfo.getTenantId(), firmwareInfo);
} catch (Exception t) { } catch (Exception t) {
@ -89,7 +90,9 @@ public class BaseFirmwareService implements FirmwareService {
try { try {
FirmwareId firmwareId = firmware.getId(); FirmwareId firmwareId = firmware.getId();
if (firmwareId != null) { 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); return firmwareDao.save(firmware.getTenantId(), firmware);
} catch (Exception t) { } catch (Exception t) {
@ -110,6 +113,7 @@ public class BaseFirmwareService implements FirmwareService {
} }
@Override @Override
@Cacheable(cacheNames = FIRMWARE_CACHE, key = "{#firmwareId}")
public FirmwareInfo findFirmwareInfoById(TenantId tenantId, FirmwareId firmwareId) { public FirmwareInfo findFirmwareInfoById(TenantId tenantId, FirmwareId firmwareId) {
log.trace("Executing findFirmwareInfoById [{}]", firmwareId); log.trace("Executing findFirmwareInfoById [{}]", firmwareId);
validateId(firmwareId, INCORRECT_FIRMWARE_ID + firmwareId); validateId(firmwareId, INCORRECT_FIRMWARE_ID + firmwareId);
@ -144,7 +148,8 @@ public class BaseFirmwareService implements FirmwareService {
validateId(firmwareId, INCORRECT_FIRMWARE_ID + firmwareId); validateId(firmwareId, INCORRECT_FIRMWARE_ID + firmwareId);
try { try {
Cache cache = cacheManager.getCache(FIRMWARE_CACHE); Cache cache = cacheManager.getCache(FIRMWARE_CACHE);
cache.evict(Collections.singletonList(firmwareId)); cache.evict(toFirmwareInfoKey(firmwareId));
firmwareDataCache.evict(firmwareId.toString());
firmwareDao.removeById(tenantId, firmwareId.getId()); firmwareDao.removeById(tenantId, firmwareId.getId());
} catch (Exception t) { } catch (Exception t) {
ConstraintViolationException e = extractConstraintViolationException(t).orElse(null); ConstraintViolationException e = extractConstraintViolationException(t).orElse(null);
@ -323,4 +328,9 @@ public class BaseFirmwareService implements FirmwareService {
return Optional.empty(); return Optional.empty();
} }
} }
private static List<FirmwareId> toFirmwareInfoKey(FirmwareId firmwareId) {
return Collections.singletonList(firmwareId);
}
} }

View File

@ -30,6 +30,7 @@ import org.thingsboard.server.dao.util.mapping.JsonStringType;
import javax.persistence.Column; import javax.persistence.Column;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.Lob;
import javax.persistence.Table; import javax.persistence.Table;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.UUID; import java.util.UUID;
@ -74,6 +75,7 @@ public class FirmwareEntity extends BaseSqlEntity<Firmware> implements SearchTex
@Column(name = FIRMWARE_CHECKSUM_COLUMN) @Column(name = FIRMWARE_CHECKSUM_COLUMN)
private String checksum; private String checksum;
@Lob
@Column(name = FIRMWARE_DATA_COLUMN, columnDefinition = "BINARY") @Column(name = FIRMWARE_DATA_COLUMN, columnDefinition = "BINARY")
private byte[] data; private byte[] data;

View File

@ -35,6 +35,7 @@ import org.thingsboard.server.dao.device.DeviceService;
import org.thingsboard.server.dao.entity.AbstractEntityService; import org.thingsboard.server.dao.entity.AbstractEntityService;
import org.thingsboard.server.dao.entityview.EntityViewService; import org.thingsboard.server.dao.entityview.EntityViewService;
import org.thingsboard.server.dao.exception.DataValidationException; 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.resource.ResourceService;
import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.dao.rule.RuleChainService;
import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.service.DataValidator;
@ -92,6 +93,9 @@ public class TenantServiceImpl extends AbstractEntityService implements TenantSe
@Autowired @Autowired
private ResourceService resourceService; private ResourceService resourceService;
@Autowired
private FirmwareService firmwareService;
@Override @Override
public Tenant findTenantById(TenantId tenantId) { public Tenant findTenantById(TenantId tenantId) {
log.trace("Executing findTenantById [{}]", tenantId); log.trace("Executing findTenantById [{}]", tenantId);
@ -146,6 +150,7 @@ public class TenantServiceImpl extends AbstractEntityService implements TenantSe
ruleChainService.deleteRuleChainsByTenantId(tenantId); ruleChainService.deleteRuleChainsByTenantId(tenantId);
apiUsageStateService.deleteApiUsageStateByTenantId(tenantId); apiUsageStateService.deleteApiUsageStateByTenantId(tenantId);
resourceService.deleteResourcesByTenantId(tenantId); resourceService.deleteResourcesByTenantId(tenantId);
firmwareService.deleteFirmwaresByTenantId(tenantId);
tenantDao.removeById(tenantId, tenantId.getId()); tenantDao.removeById(tenantId, tenantId.getId());
deleteEntityRelations(tenantId, tenantId); deleteEntityRelations(tenantId, tenantId);
} }

View File

@ -186,7 +186,7 @@ CREATE TABLE IF NOT EXISTS firmware (
content_type varchar(255), content_type varchar(255),
checksum_algorithm varchar(32), checksum_algorithm varchar(32),
checksum varchar(1020), checksum varchar(1020),
data bytea, data oid,
data_size bigint, data_size bigint,
additional_info varchar, additional_info varchar,
search_text varchar(255), search_text varchar(255),