Merge TS fix using cherry-pick
This commit is contained in:
		
							parent
							
								
									5834bbd75d
								
							
						
					
					
						commit
						6d20ca441e
					
				@ -0,0 +1,114 @@
 | 
			
		||||
package org.thingsboard.server.transport.lwm2m.server;
 | 
			
		||||
 | 
			
		||||
import org.junit.jupiter.api.BeforeEach;
 | 
			
		||||
import org.junit.jupiter.api.Test;
 | 
			
		||||
import org.thingsboard.server.gen.transport.TransportProtos;
 | 
			
		||||
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.concurrent.ConcurrentHashMap;
 | 
			
		||||
import java.util.concurrent.ConcurrentMap;
 | 
			
		||||
import java.util.concurrent.TimeUnit;
 | 
			
		||||
import java.util.concurrent.atomic.AtomicLong;
 | 
			
		||||
 | 
			
		||||
import static java.util.Collections.emptyList;
 | 
			
		||||
import static org.assertj.core.api.Assertions.assertThat;
 | 
			
		||||
import static org.mockito.ArgumentMatchers.any;
 | 
			
		||||
import static org.mockito.ArgumentMatchers.anyLong;
 | 
			
		||||
import static org.mockito.ArgumentMatchers.anyString;
 | 
			
		||||
import static org.mockito.BDDMockito.willReturn;
 | 
			
		||||
import static org.mockito.Mockito.mock;
 | 
			
		||||
import static org.mockito.Mockito.never;
 | 
			
		||||
import static org.mockito.Mockito.spy;
 | 
			
		||||
import static org.mockito.Mockito.times;
 | 
			
		||||
import static org.mockito.Mockito.verify;
 | 
			
		||||
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_TELEMETRY;
 | 
			
		||||
 | 
			
		||||
class LwM2mTransportServerHelperTest {
 | 
			
		||||
 | 
			
		||||
    public static final String KEY_SW_STATE = "sw_state";
 | 
			
		||||
    public static final String DOWNLOADING = "DOWNLOADING";
 | 
			
		||||
 | 
			
		||||
    long now;
 | 
			
		||||
    List<TransportProtos.KeyValueProto> kvList;
 | 
			
		||||
    ConcurrentMap<String, AtomicLong> keyTsLatestMap;
 | 
			
		||||
    LwM2mTransportServerHelper helper;
 | 
			
		||||
    LwM2mTransportContext context;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    @BeforeEach
 | 
			
		||||
    void setUp() {
 | 
			
		||||
        now = System.currentTimeMillis();
 | 
			
		||||
        context = mock(LwM2mTransportContext.class);
 | 
			
		||||
        helper = spy(new LwM2mTransportServerHelper(context));
 | 
			
		||||
        willReturn(now).given(helper).getCurrentTimeMillis();
 | 
			
		||||
        kvList = List.of(
 | 
			
		||||
                TransportProtos.KeyValueProto.newBuilder().setKey(KEY_SW_STATE).setStringV(DOWNLOADING).build(),
 | 
			
		||||
                TransportProtos.KeyValueProto.newBuilder().setKey(LOG_LWM2M_TELEMETRY).setStringV("Transport log example").build()
 | 
			
		||||
        );
 | 
			
		||||
        keyTsLatestMap = new ConcurrentHashMap<>();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    void givenKeyAndLatestTsMapAndCurrentTs_whenGetTs_thenVerifyNoGetTsByKeyCall() {
 | 
			
		||||
        assertThat(helper.getTs(null, null)).isEqualTo(now);
 | 
			
		||||
        assertThat(helper.getTs(null, keyTsLatestMap)).isEqualTo(now);
 | 
			
		||||
        assertThat(helper.getTs(emptyList(), null)).isEqualTo(now);
 | 
			
		||||
        assertThat(helper.getTs(emptyList(), keyTsLatestMap)).isEqualTo(now);
 | 
			
		||||
        assertThat(helper.getTs(kvList, null)).isEqualTo(now);
 | 
			
		||||
 | 
			
		||||
        verify(helper, never()).getTsByKey(anyString(), any(ConcurrentMap.class), anyLong());
 | 
			
		||||
        verify(helper, times(5)).getCurrentTimeMillis();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    void givenKeyAndLatestTsMapAndCurrentTs_whenGetTs_thenVerifyGetTsByKeyCallByFirstKey() {
 | 
			
		||||
        assertThat(helper.getTs(kvList, keyTsLatestMap)).isEqualTo(now);
 | 
			
		||||
 | 
			
		||||
        verify(helper, times(1)).getTsByKey(kvList.get(0).getKey(), keyTsLatestMap, now);
 | 
			
		||||
        verify(helper, times(1)).getTsByKey(anyString(), any(ConcurrentMap.class), anyLong());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    void givenKeyAndEmptyLatestTsMap_whenGetTsByKey_thenAddToMapAndReturnNow() {
 | 
			
		||||
        assertThat(keyTsLatestMap).as("ts latest map before").isEmpty();
 | 
			
		||||
 | 
			
		||||
        assertThat(helper.getTsByKey(KEY_SW_STATE, keyTsLatestMap, now)).as("getTsByKey").isEqualTo(now);
 | 
			
		||||
 | 
			
		||||
        assertThat(keyTsLatestMap).as("ts latest map after").hasSize(1);
 | 
			
		||||
        assertThat(keyTsLatestMap.get(KEY_SW_STATE)).as("key present").isNotNull();
 | 
			
		||||
        assertThat(keyTsLatestMap.get(KEY_SW_STATE).get()).as("ts in map by key").isEqualTo(now);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    void givenKeyAndLatestTsMapWithExistedKey_whenGetTsByKey_thenCallSwapOrIncrementMethod() {
 | 
			
		||||
        keyTsLatestMap.put(KEY_SW_STATE, new AtomicLong());
 | 
			
		||||
        keyTsLatestMap.put("other", new AtomicLong());
 | 
			
		||||
        assertThat(keyTsLatestMap).as("ts latest map").hasSize(2);
 | 
			
		||||
        willReturn(now).given(helper).compareAndSwapOrIncrementTsAtomically(any(AtomicLong.class), anyLong());
 | 
			
		||||
 | 
			
		||||
        assertThat(helper.getTsByKey(KEY_SW_STATE, keyTsLatestMap, now)).as("getTsByKey").isEqualTo(now);
 | 
			
		||||
 | 
			
		||||
        verify(helper, times(1)).compareAndSwapOrIncrementTsAtomically(keyTsLatestMap.get(KEY_SW_STATE), now);
 | 
			
		||||
        verify(helper, times(1)).compareAndSwapOrIncrementTsAtomically(any(AtomicLong.class), anyLong());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    void givenMapWithTsValueLessThanNow_whenCompareAndSwapOrIncrementTsAtomically_thenReturnNow() {
 | 
			
		||||
        keyTsLatestMap.put(KEY_SW_STATE, new AtomicLong(now - 1));
 | 
			
		||||
        assertThat(helper.compareAndSwapOrIncrementTsAtomically(keyTsLatestMap.get(KEY_SW_STATE), now)).isEqualTo(now);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    void givenMapWithTsValueEqualsNow_whenCompareAndSwapOrIncrementTsAtomically_thenReturnNowIncremented() {
 | 
			
		||||
        keyTsLatestMap.put(KEY_SW_STATE, new AtomicLong(now));
 | 
			
		||||
        assertThat(helper.compareAndSwapOrIncrementTsAtomically(keyTsLatestMap.get(KEY_SW_STATE), now)).isEqualTo(now + 1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    void givenMapWithTsValueGreaterThanNow_whenCompareAndSwapOrIncrementTsAtomically_thenReturnGreaterThanNowIncremented() {
 | 
			
		||||
        final long nextHourTs = now + TimeUnit.HOURS.toMillis(1);
 | 
			
		||||
        keyTsLatestMap.put(KEY_SW_STATE, new AtomicLong(nextHourTs));
 | 
			
		||||
        assertThat(helper.compareAndSwapOrIncrementTsAtomically(keyTsLatestMap.get(KEY_SW_STATE), now)).isEqualTo(nextHourTs + 1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -14,21 +14,6 @@
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
package org.thingsboard.server.transport.lwm2m.server;
 | 
			
		||||
/**
 | 
			
		||||
 * Copyright © 2016-2020 The Thingsboard Authors
 | 
			
		||||
 * <p>
 | 
			
		||||
 * 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
 | 
			
		||||
 * <p>
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 * <p>
 | 
			
		||||
 * 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.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
import lombok.RequiredArgsConstructor;
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
@ -37,10 +22,7 @@ import org.eclipse.leshan.core.model.DefaultDDFFileValidator;
 | 
			
		||||
import org.eclipse.leshan.core.model.InvalidDDFFileException;
 | 
			
		||||
import org.eclipse.leshan.core.model.ObjectModel;
 | 
			
		||||
import org.eclipse.leshan.core.model.ResourceModel;
 | 
			
		||||
import org.eclipse.leshan.core.node.LwM2mPath;
 | 
			
		||||
import org.eclipse.leshan.core.node.LwM2mResource;
 | 
			
		||||
import org.eclipse.leshan.core.node.codec.CodecException;
 | 
			
		||||
import org.eclipse.leshan.core.request.ContentFormat;
 | 
			
		||||
import org.springframework.stereotype.Component;
 | 
			
		||||
import org.thingsboard.server.common.transport.TransportServiceCallback;
 | 
			
		||||
import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse;
 | 
			
		||||
@ -49,19 +31,17 @@ import org.thingsboard.server.gen.transport.TransportProtos.PostAttributeMsg;
 | 
			
		||||
import org.thingsboard.server.gen.transport.TransportProtos.PostTelemetryMsg;
 | 
			
		||||
import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto;
 | 
			
		||||
import org.thingsboard.server.queue.util.TbLwM2mTransportComponent;
 | 
			
		||||
import org.thingsboard.server.transport.lwm2m.server.adaptors.LwM2MJsonAdaptor;
 | 
			
		||||
import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient;
 | 
			
		||||
import org.thingsboard.server.transport.lwm2m.server.client.ResourceValue;
 | 
			
		||||
 | 
			
		||||
import javax.annotation.Nonnull;
 | 
			
		||||
import javax.annotation.Nullable;
 | 
			
		||||
import java.io.ByteArrayInputStream;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.concurrent.TimeUnit;
 | 
			
		||||
import java.util.concurrent.atomic.AtomicInteger;
 | 
			
		||||
import java.util.concurrent.ConcurrentMap;
 | 
			
		||||
import java.util.concurrent.atomic.AtomicLong;
 | 
			
		||||
 | 
			
		||||
import static org.thingsboard.server.gen.transport.TransportProtos.KeyValueType.BOOLEAN_V;
 | 
			
		||||
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.fromVersionedIdToObjectId;
 | 
			
		||||
 | 
			
		||||
@Slf4j
 | 
			
		||||
@Component
 | 
			
		||||
@ -70,11 +50,6 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.f
 | 
			
		||||
public class LwM2mTransportServerHelper {
 | 
			
		||||
 | 
			
		||||
    private final LwM2mTransportContext context;
 | 
			
		||||
    private final AtomicInteger atomicTs = new AtomicInteger(0);
 | 
			
		||||
 | 
			
		||||
    public long getTS() {
 | 
			
		||||
        return TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()) * 1000L + (atomicTs.getAndIncrement() % 1000);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void sendParametersOnThingsboardAttribute(List<TransportProtos.KeyValueProto> result, SessionInfoProto sessionInfo) {
 | 
			
		||||
        PostAttributeMsg.Builder request = PostAttributeMsg.newBuilder();
 | 
			
		||||
@ -83,16 +58,67 @@ public class LwM2mTransportServerHelper {
 | 
			
		||||
        context.getTransportService().process(sessionInfo, postAttributeMsg, TransportServiceCallback.EMPTY);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void sendParametersOnThingsboardTelemetry(List<TransportProtos.KeyValueProto> result, SessionInfoProto sessionInfo) {
 | 
			
		||||
        PostTelemetryMsg.Builder request = PostTelemetryMsg.newBuilder();
 | 
			
		||||
        TransportProtos.TsKvListProto.Builder builder = TransportProtos.TsKvListProto.newBuilder();
 | 
			
		||||
        builder.setTs(this.getTS());
 | 
			
		||||
        builder.addAllKv(result);
 | 
			
		||||
        request.addTsKvList(builder.build());
 | 
			
		||||
        PostTelemetryMsg postTelemetryMsg = request.build();
 | 
			
		||||
    public void sendParametersOnThingsboardTelemetry(List<TransportProtos.KeyValueProto> kvList, SessionInfoProto sessionInfo) {
 | 
			
		||||
        sendParametersOnThingsboardTelemetry(kvList, sessionInfo, null);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void sendParametersOnThingsboardTelemetry(List<TransportProtos.KeyValueProto> kvList, SessionInfoProto sessionInfo, @Nullable ConcurrentMap<String, AtomicLong> keyTsLatestMap) {
 | 
			
		||||
        TransportProtos.TsKvListProto tsKvList = toTsKvList(kvList, keyTsLatestMap);
 | 
			
		||||
 | 
			
		||||
        PostTelemetryMsg postTelemetryMsg = PostTelemetryMsg.newBuilder()
 | 
			
		||||
                .addTsKvList(tsKvList)
 | 
			
		||||
                .build();
 | 
			
		||||
 | 
			
		||||
        context.getTransportService().process(sessionInfo, postTelemetryMsg, TransportServiceCallback.EMPTY);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    TransportProtos.TsKvListProto toTsKvList(List<TransportProtos.KeyValueProto> kvList, ConcurrentMap<String, AtomicLong> keyTsLatestMap) {
 | 
			
		||||
        return TransportProtos.TsKvListProto.newBuilder()
 | 
			
		||||
                .setTs(getTs(kvList, keyTsLatestMap))
 | 
			
		||||
                .addAllKv(kvList)
 | 
			
		||||
                .build();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    long getTs(List<TransportProtos.KeyValueProto> kvList, ConcurrentMap<String, AtomicLong> keyTsLatestMap) {
 | 
			
		||||
        if (keyTsLatestMap == null || kvList == null || kvList.isEmpty()) {
 | 
			
		||||
            return getCurrentTimeMillis();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return getTsByKey(kvList.get(0).getKey(), keyTsLatestMap, getCurrentTimeMillis());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    long getTsByKey(@Nonnull String key, @Nonnull ConcurrentMap<String, AtomicLong> keyTsLatestMap, final long tsNow) {
 | 
			
		||||
        AtomicLong tsLatestAtomic = keyTsLatestMap.putIfAbsent(key, new AtomicLong(tsNow));
 | 
			
		||||
        if (tsLatestAtomic == null) {
 | 
			
		||||
            return tsNow; // it is a first known timestamp for this key. return as the latest
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return compareAndSwapOrIncrementTsAtomically(tsLatestAtomic, tsNow);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * This algorithm is sensitive to wall-clock time shift.
 | 
			
		||||
     * Once time have shifted *backward*, the latest ts never came back.
 | 
			
		||||
     * Ts latest will be incremented until current time overtake the latest ts.
 | 
			
		||||
     * In normal environment without race conditions method will return current ts (wall-clock)
 | 
			
		||||
     * */
 | 
			
		||||
    long compareAndSwapOrIncrementTsAtomically(AtomicLong tsLatestAtomic, final long tsNow) {
 | 
			
		||||
        long tsLatest;
 | 
			
		||||
        while ((tsLatest = tsLatestAtomic.get()) < tsNow) {
 | 
			
		||||
            if (tsLatestAtomic.compareAndSet(tsLatest, tsNow)) {
 | 
			
		||||
                return tsNow; //swap successful
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return tsLatestAtomic.incrementAndGet(); //return next ms
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * For the test ability to mock system timer
 | 
			
		||||
     * */
 | 
			
		||||
    long getCurrentTimeMillis() {
 | 
			
		||||
        return System.currentTimeMillis();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @return - sessionInfo after access connect client
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
@ -50,8 +50,10 @@ import java.util.Optional;
 | 
			
		||||
import java.util.Set;
 | 
			
		||||
import java.util.UUID;
 | 
			
		||||
import java.util.concurrent.ConcurrentHashMap;
 | 
			
		||||
import java.util.concurrent.ConcurrentMap;
 | 
			
		||||
import java.util.concurrent.Future;
 | 
			
		||||
import java.util.concurrent.atomic.AtomicInteger;
 | 
			
		||||
import java.util.concurrent.atomic.AtomicLong;
 | 
			
		||||
import java.util.concurrent.locks.Lock;
 | 
			
		||||
import java.util.concurrent.locks.ReentrantLock;
 | 
			
		||||
import java.util.stream.Collectors;
 | 
			
		||||
@ -80,6 +82,8 @@ public class LwM2mClient implements Serializable {
 | 
			
		||||
    private final Map<String, ResourceValue> resources;
 | 
			
		||||
    @Getter
 | 
			
		||||
    private final Map<String, TsKvProto> sharedAttributes;
 | 
			
		||||
    @Getter
 | 
			
		||||
    private final ConcurrentMap<String, AtomicLong> keyTsLatestMap;
 | 
			
		||||
 | 
			
		||||
    @Getter
 | 
			
		||||
    private TenantId tenantId;
 | 
			
		||||
@ -126,6 +130,7 @@ public class LwM2mClient implements Serializable {
 | 
			
		||||
        this.endpoint = endpoint;
 | 
			
		||||
        this.sharedAttributes = new ConcurrentHashMap<>();
 | 
			
		||||
        this.resources = new ConcurrentHashMap<>();
 | 
			
		||||
        this.keyTsLatestMap = new ConcurrentHashMap<>();
 | 
			
		||||
        this.state = LwM2MClientState.CREATED;
 | 
			
		||||
        this.lock = new ReentrantLock();
 | 
			
		||||
        this.retryAttempts = new AtomicInteger(0);
 | 
			
		||||
 | 
			
		||||
@ -39,7 +39,7 @@ public class DefaultLwM2MTelemetryLogService implements LwM2MTelemetryLogService
 | 
			
		||||
            if (logMsg.length() > 1024) {
 | 
			
		||||
                logMsg = logMsg.substring(0, 1024);
 | 
			
		||||
            }
 | 
			
		||||
            this.helper.sendParametersOnThingsboardTelemetry(this.helper.getKvStringtoThingsboard(LOG_LWM2M_TELEMETRY, logMsg), client.getSession());
 | 
			
		||||
            this.helper.sendParametersOnThingsboardTelemetry(this.helper.getKvStringtoThingsboard(LOG_LWM2M_TELEMETRY, logMsg), client.getSession(), client.getKeyTsLatestMap());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -602,7 +602,7 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl
 | 
			
		||||
        kvProto = TransportProtos.KeyValueProto.newBuilder().setKey(LOG_LWM2M_TELEMETRY);
 | 
			
		||||
        kvProto.setType(TransportProtos.KeyValueType.STRING_V).setStringV(log);
 | 
			
		||||
        result.add(kvProto.build());
 | 
			
		||||
        helper.sendParametersOnThingsboardTelemetry(result, client.getSession());
 | 
			
		||||
        helper.sendParametersOnThingsboardTelemetry(result, client.getSession(), client.getKeyTsLatestMap());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static Optional<OtaPackageUpdateStatus> toOtaPackageUpdateStatus(FirmwareUpdateResult fwUpdateResult) {
 | 
			
		||||
 | 
			
		||||
@ -19,12 +19,14 @@ import com.fasterxml.jackson.annotation.JsonIgnore;
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
import lombok.EqualsAndHashCode;
 | 
			
		||||
import lombok.NoArgsConstructor;
 | 
			
		||||
import lombok.ToString;
 | 
			
		||||
import org.thingsboard.server.common.data.ota.OtaPackageType;
 | 
			
		||||
import org.thingsboard.server.transport.lwm2m.server.ota.LwM2MClientOtaInfo;
 | 
			
		||||
 | 
			
		||||
@Data
 | 
			
		||||
@EqualsAndHashCode(callSuper = true)
 | 
			
		||||
@NoArgsConstructor
 | 
			
		||||
@ToString(callSuper = true)
 | 
			
		||||
public class LwM2MClientFwOtaInfo extends LwM2MClientOtaInfo<LwM2MFirmwareUpdateStrategy, FirmwareUpdateState, FirmwareUpdateResult> {
 | 
			
		||||
 | 
			
		||||
    private Integer deliveryMethod;
 | 
			
		||||
 | 
			
		||||
@ -19,6 +19,7 @@ import com.fasterxml.jackson.annotation.JsonIgnore;
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
import lombok.EqualsAndHashCode;
 | 
			
		||||
import lombok.NoArgsConstructor;
 | 
			
		||||
import lombok.ToString;
 | 
			
		||||
import org.thingsboard.server.common.data.StringUtils;
 | 
			
		||||
import org.thingsboard.server.common.data.ota.OtaPackageType;
 | 
			
		||||
import org.thingsboard.server.transport.lwm2m.server.ota.LwM2MClientOtaInfo;
 | 
			
		||||
@ -29,6 +30,7 @@ import org.thingsboard.server.transport.lwm2m.server.ota.firmware.LwM2MFirmwareU
 | 
			
		||||
@Data
 | 
			
		||||
@EqualsAndHashCode(callSuper = true)
 | 
			
		||||
@NoArgsConstructor
 | 
			
		||||
@ToString(callSuper = true)
 | 
			
		||||
public class LwM2MClientSwOtaInfo extends LwM2MClientOtaInfo<LwM2MSoftwareUpdateStrategy, SoftwareUpdateState, SoftwareUpdateResult> {
 | 
			
		||||
 | 
			
		||||
    public LwM2MClientSwOtaInfo(String endpoint, String baseUrl, LwM2MSoftwareUpdateStrategy strategy) {
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user