CoAP support for firmware updates
This commit is contained in:
		
						commit
						edc993427e
					
				@ -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),
 | 
			
		||||
 | 
			
		||||
@ -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);
 | 
			
		||||
 | 
			
		||||
@ -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<String, ReentrantLock> 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());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -372,8 +372,8 @@ caffeine:
 | 
			
		||||
      timeToLiveInMinutes: 20000
 | 
			
		||||
      maxSize: 10000
 | 
			
		||||
    firmwares:
 | 
			
		||||
      timeToLiveInMinutes: 1440
 | 
			
		||||
      maxSize: 100
 | 
			
		||||
      timeToLiveInMinutes: 60
 | 
			
		||||
      maxSize: 10
 | 
			
		||||
    edges:
 | 
			
		||||
      timeToLiveInMinutes: 1440
 | 
			
		||||
      maxSize: 0
 | 
			
		||||
 | 
			
		||||
@ -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();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -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);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -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);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -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);
 | 
			
		||||
}
 | 
			
		||||
@ -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);
 | 
			
		||||
}
 | 
			
		||||
@ -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);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -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();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -30,7 +30,9 @@ public enum SessionMsgType {
 | 
			
		||||
 | 
			
		||||
    SESSION_OPEN, SESSION_CLOSE,
 | 
			
		||||
 | 
			
		||||
    CLAIM_REQUEST();
 | 
			
		||||
    CLAIM_REQUEST(),
 | 
			
		||||
 | 
			
		||||
    GET_FIRMWARE_REQUEST;
 | 
			
		||||
 | 
			
		||||
    private final boolean requiresRulesProcessing;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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<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 final CoapExchange exchange;
 | 
			
		||||
 | 
			
		||||
@ -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;
 | 
			
		||||
 | 
			
		||||
@ -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<ByteArrayResource> response = ResponseEntity.ok()
 | 
			
		||||
                        .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + firmwareResponseMsg.getFileName())
 | 
			
		||||
                        .header("x-filename", firmwareResponseMsg.getFileName())
 | 
			
		||||
 | 
			
		||||
@ -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);
 | 
			
		||||
 | 
			
		||||
@ -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() {
 | 
			
		||||
 | 
			
		||||
@ -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<FirmwareId> toFirmwareInfoKey(FirmwareId firmwareId) {
 | 
			
		||||
        return Collections.singletonList(firmwareId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -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<Firmware> implements SearchTex
 | 
			
		||||
    @Column(name = FIRMWARE_CHECKSUM_COLUMN)
 | 
			
		||||
    private String checksum;
 | 
			
		||||
 | 
			
		||||
    @Lob
 | 
			
		||||
    @Column(name = FIRMWARE_DATA_COLUMN, columnDefinition = "BINARY")
 | 
			
		||||
    private byte[] data;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -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),
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user