Browse Source

CoAP support for firmware updates

pull/4505/head
Andrii Shvaika 5 years ago
parent
commit
edc993427e
  1. 2
      application/src/main/data/upgrade/3.2.2/schema_update.sql
  2. 4
      application/src/main/java/org/thingsboard/server/service/firmware/DefaultFirmwareStateService.java
  3. 12
      application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java
  4. 4
      application/src/main/resources/thingsboard.yml
  5. 33
      common/cache/src/main/java/org/thingsboard/server/cache/firmware/AbstractRedisFirmwareCache.java
  6. 22
      common/cache/src/main/java/org/thingsboard/server/cache/firmware/CaffeineFirmwareCache.java
  7. 38
      common/cache/src/main/java/org/thingsboard/server/cache/firmware/CaffeineFirmwareCacheWriter.java
  8. 20
      common/cache/src/main/java/org/thingsboard/server/cache/firmware/FirmwareCacheWriter.java
  9. 7
      common/cache/src/main/java/org/thingsboard/server/cache/firmware/FirmwareDataCache.java
  10. 38
      common/cache/src/main/java/org/thingsboard/server/cache/firmware/RedisFirmwareCacheWriter.java
  11. 31
      common/cache/src/main/java/org/thingsboard/server/cache/firmware/RedisFirmwareDataCache.java
  12. 2
      common/message/src/main/java/org/thingsboard/server/common/msg/session/FeatureType.java
  13. 4
      common/message/src/main/java/org/thingsboard/server/common/msg/session/SessionMsgType.java
  14. 48
      common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java
  15. 1
      common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportService.java
  16. 2
      common/transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java
  17. 2
      common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java
  18. 4
      common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportContext.java
  19. 30
      dao/src/main/java/org/thingsboard/server/dao/firmware/BaseFirmwareService.java
  20. 2
      dao/src/main/java/org/thingsboard/server/dao/model/sql/FirmwareEntity.java
  21. 5
      dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java
  22. 2
      dao/src/main/resources/sql/schema-entities.sql

2
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), 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),

4
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.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);

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

4
application/src/main/resources/thingsboard.yml

@ -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

33
common/cache/src/main/java/org/thingsboard/server/cache/firmware/AbstractRedisFirmwareCache.java

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

22
common/cache/src/main/java/org/thingsboard/server/cache/firmware/CaffeineFirmwareCacheReader.java → common/cache/src/main/java/org/thingsboard/server/cache/firmware/CaffeineFirmwareCache.java

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

38
common/cache/src/main/java/org/thingsboard/server/cache/firmware/CaffeineFirmwareCacheWriter.java

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

20
common/cache/src/main/java/org/thingsboard/server/cache/firmware/FirmwareCacheWriter.java

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

7
common/cache/src/main/java/org/thingsboard/server/cache/firmware/FirmwareCacheReader.java → common/cache/src/main/java/org/thingsboard/server/cache/firmware/FirmwareDataCache.java

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

38
common/cache/src/main/java/org/thingsboard/server/cache/firmware/RedisFirmwareCacheWriter.java

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

31
common/cache/src/main/java/org/thingsboard/server/cache/firmware/RedisFirmwareCacheReader.java → common/cache/src/main/java/org/thingsboard/server/cache/firmware/RedisFirmwareDataCache.java

@ -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;
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}'=='redis'") @ConditionalOnProperty(prefix = "cache", value = "type", havingValue = "redis")
public class RedisFirmwareCacheReader extends AbstractRedisFirmwareCache implements FirmwareCacheReader { @RequiredArgsConstructor
public class RedisFirmwareDataCache implements FirmwareDataCache {
public RedisFirmwareCacheReader(RedisConnectionFactory redisConnectionFactory) { private final RedisConnectionFactory redisConnectionFactory;
super(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();
}
} }

2
common/message/src/main/java/org/thingsboard/server/common/msg/session/FeatureType.java

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

4
common/message/src/main/java/org/thingsboard/server/common/msg/session/SessionMsgType.java

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

48
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 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;

1
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 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;

2
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)); 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())

2
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); 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);

4
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.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() {

30
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.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);
}
} }

2
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.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;

5
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.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);
} }

2
dao/src/main/resources/sql/schema-entities.sql

@ -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),

Loading…
Cancel
Save