Browse Source
# Conflicts: # application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.javapull/5990/head
219 changed files with 7651 additions and 4895 deletions
File diff suppressed because one or more lines are too long
@ -0,0 +1,50 @@ |
|||
-- |
|||
-- Copyright © 2016-2022 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. |
|||
-- |
|||
|
|||
|
|||
DROP PROCEDURE IF EXISTS public.cleanup_events_by_ttl(bigint, bigint, bigint); |
|||
|
|||
CREATE OR REPLACE PROCEDURE cleanup_events_by_ttl( |
|||
IN regular_events_start_ts bigint, |
|||
IN regular_events_end_ts bigint, |
|||
IN debug_events_start_ts bigint, |
|||
IN debug_events_end_ts bigint, |
|||
INOUT deleted bigint) |
|||
LANGUAGE plpgsql AS |
|||
$$ |
|||
DECLARE |
|||
ttl_deleted_count bigint DEFAULT 0; |
|||
debug_ttl_deleted_count bigint DEFAULT 0; |
|||
BEGIN |
|||
IF regular_events_start_ts > 0 AND regular_events_end_ts > 0 THEN |
|||
EXECUTE format( |
|||
'WITH deleted AS (DELETE FROM event WHERE id in (SELECT id from event WHERE ts > %L::bigint AND ts < %L::bigint AND ' || |
|||
'(event_type != %L::varchar AND event_type != %L::varchar)) RETURNING *) ' || |
|||
'SELECT count(*) FROM deleted', regular_events_start_ts, regular_events_end_ts, |
|||
'DEBUG_RULE_NODE', 'DEBUG_RULE_CHAIN') into ttl_deleted_count; |
|||
END IF; |
|||
IF debug_events_start_ts > 0 AND debug_events_end_ts > 0 THEN |
|||
EXECUTE format( |
|||
'WITH deleted AS (DELETE FROM event WHERE id in (SELECT id from event WHERE ts > %L::bigint AND ts < %L::bigint AND ' || |
|||
'(event_type = %L::varchar OR event_type = %L::varchar)) RETURNING *) ' || |
|||
'SELECT count(*) FROM deleted', debug_events_start_ts, debug_events_end_ts, |
|||
'DEBUG_RULE_NODE', 'DEBUG_RULE_CHAIN') into debug_ttl_deleted_count; |
|||
END IF; |
|||
RAISE NOTICE 'Events removed by ttl: %', ttl_deleted_count; |
|||
RAISE NOTICE 'Debug Events removed by ttl: %', debug_ttl_deleted_count; |
|||
deleted := ttl_deleted_count + debug_ttl_deleted_count; |
|||
END |
|||
$$; |
|||
@ -0,0 +1,184 @@ |
|||
/* |
|||
* Licensed to the Apache Software Foundation (ASF) under one or more |
|||
* contributor license agreements. See the NOTICE file distributed with |
|||
* this work for additional information regarding copyright ownership. |
|||
* The ASF licenses this file to You 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. |
|||
*/ |
|||
|
|||
/* |
|||
* Content of this file was modified to addresses the issue https://issues.apache.org/jira/browse/KAFKA-4090
|
|||
* |
|||
*/ |
|||
package org.apache.kafka.common.network; |
|||
|
|||
import org.apache.kafka.common.memory.MemoryPool; |
|||
import org.slf4j.Logger; |
|||
import org.slf4j.LoggerFactory; |
|||
import org.thingsboard.server.common.data.exception.ThingsboardKafkaClientError; |
|||
|
|||
import java.io.EOFException; |
|||
import java.io.IOException; |
|||
import java.nio.ByteBuffer; |
|||
import java.nio.channels.ScatteringByteChannel; |
|||
import java.util.Arrays; |
|||
import java.util.stream.Collectors; |
|||
|
|||
/** |
|||
* A size delimited Receive that consists of a 4 byte network-ordered size N followed by N bytes of content |
|||
*/ |
|||
public class NetworkReceive implements Receive { |
|||
|
|||
public final static String UNKNOWN_SOURCE = ""; |
|||
public final static int UNLIMITED = -1; |
|||
public final static int TB_MAX_REQUESTED_BUFFER_SIZE = 100 * 1024 * 1024; |
|||
public final static int TB_LOG_REQUESTED_BUFFER_SIZE = 10 * 1024 * 1024; |
|||
private static final Logger log = LoggerFactory.getLogger(NetworkReceive.class); |
|||
private static final ByteBuffer EMPTY_BUFFER = ByteBuffer.allocate(0); |
|||
|
|||
private final String source; |
|||
private final ByteBuffer size; |
|||
private final int maxSize; |
|||
private final MemoryPool memoryPool; |
|||
private int requestedBufferSize = -1; |
|||
private ByteBuffer buffer; |
|||
|
|||
|
|||
public NetworkReceive(String source, ByteBuffer buffer) { |
|||
this.source = source; |
|||
this.buffer = buffer; |
|||
this.size = null; |
|||
this.maxSize = TB_MAX_REQUESTED_BUFFER_SIZE; |
|||
this.memoryPool = MemoryPool.NONE; |
|||
} |
|||
|
|||
public NetworkReceive(String source) { |
|||
this.source = source; |
|||
this.size = ByteBuffer.allocate(4); |
|||
this.buffer = null; |
|||
this.maxSize = TB_MAX_REQUESTED_BUFFER_SIZE; |
|||
this.memoryPool = MemoryPool.NONE; |
|||
} |
|||
|
|||
public NetworkReceive(int maxSize, String source) { |
|||
this.source = source; |
|||
this.size = ByteBuffer.allocate(4); |
|||
this.buffer = null; |
|||
this.maxSize = getMaxSize(maxSize); |
|||
this.memoryPool = MemoryPool.NONE; |
|||
} |
|||
|
|||
public NetworkReceive(int maxSize, String source, MemoryPool memoryPool) { |
|||
this.source = source; |
|||
this.size = ByteBuffer.allocate(4); |
|||
this.buffer = null; |
|||
this.maxSize = getMaxSize(maxSize); |
|||
this.memoryPool = memoryPool; |
|||
} |
|||
|
|||
public NetworkReceive() { |
|||
this(UNKNOWN_SOURCE); |
|||
} |
|||
|
|||
@Override |
|||
public String source() { |
|||
return source; |
|||
} |
|||
|
|||
@Override |
|||
public boolean complete() { |
|||
return !size.hasRemaining() && buffer != null && !buffer.hasRemaining(); |
|||
} |
|||
|
|||
public long readFrom(ScatteringByteChannel channel) throws IOException { |
|||
int read = 0; |
|||
if (size.hasRemaining()) { |
|||
int bytesRead = channel.read(size); |
|||
if (bytesRead < 0) |
|||
throw new EOFException(); |
|||
read += bytesRead; |
|||
if (!size.hasRemaining()) { |
|||
size.rewind(); |
|||
int receiveSize = size.getInt(); |
|||
if (receiveSize < 0) |
|||
throw new InvalidReceiveException("Invalid receive (size = " + receiveSize + ")"); |
|||
if (maxSize != UNLIMITED && receiveSize > maxSize) { |
|||
throw new ThingsboardKafkaClientError("Invalid receive (size = " + receiveSize + " larger than " + maxSize + ")"); |
|||
} |
|||
requestedBufferSize = receiveSize; //may be 0 for some payloads (SASL)
|
|||
if (receiveSize == 0) { |
|||
buffer = EMPTY_BUFFER; |
|||
} |
|||
} |
|||
} |
|||
if (buffer == null && requestedBufferSize != -1) { //we know the size we want but havent been able to allocate it yet
|
|||
if (requestedBufferSize > TB_LOG_REQUESTED_BUFFER_SIZE) { |
|||
String stackTrace = Arrays.stream(Thread.currentThread().getStackTrace()).map(StackTraceElement::toString).collect(Collectors.joining("|")); |
|||
log.error("Allocating buffer of size {} for source {}", requestedBufferSize, source); |
|||
log.error("Stack Trace: {}", stackTrace); |
|||
} |
|||
buffer = memoryPool.tryAllocate(requestedBufferSize); |
|||
if (buffer == null) |
|||
log.trace("Broker low on memory - could not allocate buffer of size {} for source {}", requestedBufferSize, source); |
|||
} |
|||
if (buffer != null) { |
|||
int bytesRead = channel.read(buffer); |
|||
if (bytesRead < 0) |
|||
throw new EOFException(); |
|||
read += bytesRead; |
|||
} |
|||
|
|||
return read; |
|||
} |
|||
|
|||
@Override |
|||
public boolean requiredMemoryAmountKnown() { |
|||
return requestedBufferSize != -1; |
|||
} |
|||
|
|||
@Override |
|||
public boolean memoryAllocated() { |
|||
return buffer != null; |
|||
} |
|||
|
|||
|
|||
@Override |
|||
public void close() throws IOException { |
|||
if (buffer != null && buffer != EMPTY_BUFFER) { |
|||
memoryPool.release(buffer); |
|||
buffer = null; |
|||
} |
|||
} |
|||
|
|||
public ByteBuffer payload() { |
|||
return this.buffer; |
|||
} |
|||
|
|||
public int bytesRead() { |
|||
if (buffer == null) |
|||
return size.position(); |
|||
return buffer.position() + size.position(); |
|||
} |
|||
|
|||
/** |
|||
* Returns the total size of the receive including payload and size buffer |
|||
* for use in metrics. This is consistent with {@link NetworkSend#size()} |
|||
*/ |
|||
public int size() { |
|||
return payload().limit() + size.limit(); |
|||
} |
|||
|
|||
private int getMaxSize(int maxSize) { |
|||
return maxSize == UNLIMITED ? TB_MAX_REQUESTED_BUFFER_SIZE : Math.min(maxSize, TB_MAX_REQUESTED_BUFFER_SIZE); |
|||
} |
|||
|
|||
} |
|||
@ -1,110 +0,0 @@ |
|||
/** |
|||
* Copyright © 2016-2022 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.service.edge; |
|||
|
|||
import com.fasterxml.jackson.databind.JsonNode; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.apache.http.HttpHost; |
|||
import org.apache.http.conn.ssl.DefaultHostnameVerifier; |
|||
import org.apache.http.impl.client.CloseableHttpClient; |
|||
import org.apache.http.impl.client.HttpClients; |
|||
import org.springframework.beans.factory.annotation.Value; |
|||
import org.springframework.http.ResponseEntity; |
|||
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; |
|||
import org.springframework.http.client.SimpleClientHttpRequestFactory; |
|||
import org.springframework.stereotype.Service; |
|||
import org.springframework.web.client.RestTemplate; |
|||
import org.thingsboard.server.queue.util.TbCoreComponent; |
|||
|
|||
import javax.annotation.PostConstruct; |
|||
import java.net.InetSocketAddress; |
|||
import java.net.Proxy; |
|||
import java.util.HashMap; |
|||
import java.util.Map; |
|||
|
|||
import static org.apache.commons.lang3.StringUtils.isNotEmpty; |
|||
|
|||
@Service |
|||
@TbCoreComponent |
|||
@Slf4j |
|||
public class DefaultEdgeLicenseService implements EdgeLicenseService { |
|||
|
|||
private RestTemplate restTemplate; |
|||
|
|||
private static final String EDGE_LICENSE_SERVER_ENDPOINT = "https://license.thingsboard.io"; |
|||
|
|||
@Value("${edges.enabled:false}") |
|||
private boolean edgesEnabled; |
|||
|
|||
@PostConstruct |
|||
public void init() { |
|||
if (edgesEnabled) { |
|||
initRestTemplate(); |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
public ResponseEntity<JsonNode> checkInstance(JsonNode request) { |
|||
return this.restTemplate.postForEntity(EDGE_LICENSE_SERVER_ENDPOINT + "/api/license/checkInstance", request, JsonNode.class); |
|||
} |
|||
|
|||
@Override |
|||
public ResponseEntity<JsonNode> activateInstance(String edgeLicenseSecret, String releaseDate) { |
|||
Map<String, String> params = new HashMap<>(); |
|||
params.put("licenseSecret", edgeLicenseSecret); |
|||
params.put("releaseDate", releaseDate); |
|||
return this.restTemplate.postForEntity(EDGE_LICENSE_SERVER_ENDPOINT + "/api/license/activateInstance?licenseSecret={licenseSecret}&releaseDate={releaseDate}", null, JsonNode.class, params); |
|||
} |
|||
|
|||
private void initRestTemplate() { |
|||
boolean jdkHttpClientEnabled = isNotEmpty(System.getProperty("tb.proxy.jdk")) && System.getProperty("tb.proxy.jdk").equalsIgnoreCase("true"); |
|||
boolean systemProxyEnabled = isNotEmpty(System.getProperty("tb.proxy.system")) && System.getProperty("tb.proxy.system").equalsIgnoreCase("true"); |
|||
boolean proxyEnabled = isNotEmpty(System.getProperty("tb.proxy.host")) && isNotEmpty(System.getProperty("tb.proxy.port")); |
|||
if (jdkHttpClientEnabled) { |
|||
log.warn("Going to use plain JDK Http Client!"); |
|||
SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory(); |
|||
if (proxyEnabled) { |
|||
log.warn("Going to use Proxy Server: [{}:{}]", System.getProperty("tb.proxy.host"), System.getProperty("tb.proxy.port")); |
|||
factory.setProxy(new Proxy(Proxy.Type.HTTP, InetSocketAddress.createUnresolved(System.getProperty("tb.proxy.host"), Integer.parseInt(System.getProperty("tb.proxy.port"))))); |
|||
} |
|||
|
|||
this.restTemplate = new RestTemplate(new SimpleClientHttpRequestFactory()); |
|||
} else { |
|||
CloseableHttpClient httpClient; |
|||
HttpComponentsClientHttpRequestFactory requestFactory; |
|||
if (systemProxyEnabled) { |
|||
log.warn("Going to use System Proxy Server!"); |
|||
httpClient = HttpClients.createSystem(); |
|||
requestFactory = new HttpComponentsClientHttpRequestFactory(); |
|||
requestFactory.setHttpClient(httpClient); |
|||
this.restTemplate = new RestTemplate(requestFactory); |
|||
} else if (proxyEnabled) { |
|||
log.warn("Going to use Proxy Server: [{}:{}]", System.getProperty("tb.proxy.host"), System.getProperty("tb.proxy.port")); |
|||
httpClient = HttpClients.custom().setSSLHostnameVerifier(new DefaultHostnameVerifier()).setProxy(new HttpHost(System.getProperty("tb.proxy.host"), Integer.parseInt(System.getProperty("tb.proxy.port")), "https")).build(); |
|||
requestFactory = new HttpComponentsClientHttpRequestFactory(); |
|||
requestFactory.setHttpClient(httpClient); |
|||
this.restTemplate = new RestTemplate(requestFactory); |
|||
} else { |
|||
httpClient = HttpClients.custom().setSSLHostnameVerifier(new DefaultHostnameVerifier()).build(); |
|||
requestFactory = new HttpComponentsClientHttpRequestFactory(); |
|||
requestFactory.setHttpClient(httpClient); |
|||
this.restTemplate = new RestTemplate(requestFactory); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
@ -0,0 +1,148 @@ |
|||
/** |
|||
* Copyright © 2016-2022 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.service.partition; |
|||
|
|||
import com.google.common.util.concurrent.ListeningScheduledExecutorService; |
|||
import com.google.common.util.concurrent.MoreExecutors; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.thingsboard.common.util.ThingsBoardThreadFactory; |
|||
import org.thingsboard.server.common.data.id.EntityId; |
|||
import org.thingsboard.server.common.msg.queue.ServiceType; |
|||
import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; |
|||
import org.thingsboard.server.queue.discovery.TbApplicationEventListener; |
|||
import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent; |
|||
|
|||
import java.util.HashSet; |
|||
import java.util.Queue; |
|||
import java.util.Set; |
|||
import java.util.concurrent.ConcurrentHashMap; |
|||
import java.util.concurrent.ConcurrentLinkedQueue; |
|||
import java.util.concurrent.ConcurrentMap; |
|||
import java.util.concurrent.Executors; |
|||
|
|||
@Slf4j |
|||
public abstract class AbstractPartitionBasedService<T extends EntityId> extends TbApplicationEventListener<PartitionChangeEvent> { |
|||
|
|||
protected final ConcurrentMap<TopicPartitionInfo, Set<T>> partitionedEntities = new ConcurrentHashMap<>(); |
|||
final Queue<Set<TopicPartitionInfo>> subscribeQueue = new ConcurrentLinkedQueue<>(); |
|||
|
|||
protected ListeningScheduledExecutorService scheduledExecutor; |
|||
|
|||
abstract protected String getServiceName(); |
|||
|
|||
abstract protected String getSchedulerExecutorName(); |
|||
|
|||
abstract protected void onAddedPartitions(Set<TopicPartitionInfo> addedPartitions); |
|||
|
|||
abstract protected void cleanupEntityOnPartitionRemoval(T entityId); |
|||
|
|||
public Set<T> getPartitionedEntities(TopicPartitionInfo tpi) { |
|||
return partitionedEntities.get(tpi); |
|||
} |
|||
|
|||
protected void init() { |
|||
// Should be always single threaded due to absence of locks.
|
|||
scheduledExecutor = MoreExecutors.listeningDecorator(Executors.newSingleThreadScheduledExecutor(ThingsBoardThreadFactory.forName(getSchedulerExecutorName()))); |
|||
} |
|||
|
|||
protected ServiceType getServiceType() { |
|||
return ServiceType.TB_CORE; |
|||
} |
|||
|
|||
protected void stop() { |
|||
if (scheduledExecutor != null) { |
|||
scheduledExecutor.shutdownNow(); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* DiscoveryService will call this event from the single thread (one-by-one). |
|||
* Events order is guaranteed by DiscoveryService. |
|||
* The only concurrency is expected from the [main] thread on Application started. |
|||
* Async implementation. Locks is not allowed by design. |
|||
* Any locks or delays in this module will affect DiscoveryService and entire system |
|||
*/ |
|||
@Override |
|||
protected void onTbApplicationEvent(PartitionChangeEvent partitionChangeEvent) { |
|||
if (getServiceType().equals(partitionChangeEvent.getServiceType())) { |
|||
log.debug("onTbApplicationEvent, processing event: {}", partitionChangeEvent); |
|||
subscribeQueue.add(partitionChangeEvent.getPartitions()); |
|||
scheduledExecutor.submit(this::pollInitStateFromDB); |
|||
} |
|||
} |
|||
|
|||
protected void pollInitStateFromDB() { |
|||
final Set<TopicPartitionInfo> partitions = getLatestPartitions(); |
|||
if (partitions == null) { |
|||
log.debug("Nothing to do. Partitions are empty."); |
|||
return; |
|||
} |
|||
initStateFromDB(partitions); |
|||
} |
|||
|
|||
private void initStateFromDB(Set<TopicPartitionInfo> partitions) { |
|||
try { |
|||
log.info("[{}] CURRENT PARTITIONS: {}", getServiceName(), partitionedEntities.keySet()); |
|||
log.info("[{}] NEW PARTITIONS: {}", getServiceName(), partitions); |
|||
|
|||
Set<TopicPartitionInfo> addedPartitions = new HashSet<>(partitions); |
|||
addedPartitions.removeAll(partitionedEntities.keySet()); |
|||
|
|||
log.info("[{}] ADDED PARTITIONS: {}", getServiceName(), addedPartitions); |
|||
|
|||
Set<TopicPartitionInfo> removedPartitions = new HashSet<>(partitionedEntities.keySet()); |
|||
removedPartitions.removeAll(partitions); |
|||
|
|||
log.info("[{}] REMOVED PARTITIONS: {}", getServiceName(), removedPartitions); |
|||
|
|||
// We no longer manage current partition of entities;
|
|||
removedPartitions.forEach(partition -> { |
|||
Set<T> entities = partitionedEntities.remove(partition); |
|||
entities.forEach(this::cleanupEntityOnPartitionRemoval); |
|||
}); |
|||
|
|||
onRepartitionEvent(); |
|||
|
|||
addedPartitions.forEach(tpi -> partitionedEntities.computeIfAbsent(tpi, key -> ConcurrentHashMap.newKeySet())); |
|||
|
|||
if (!addedPartitions.isEmpty()) { |
|||
onAddedPartitions(addedPartitions); |
|||
} |
|||
|
|||
log.info("[{}] Managing following partitions:", getServiceName()); |
|||
partitionedEntities.forEach((tpi, entities) -> { |
|||
log.info("[{}][{}]: {} entities", getServiceName(), tpi.getFullTopicName(), entities.size()); |
|||
}); |
|||
} catch (Throwable t) { |
|||
log.warn("[{}] Failed to init entities state from DB", getServiceName(), t); |
|||
} |
|||
} |
|||
|
|||
protected void onRepartitionEvent() { |
|||
} |
|||
|
|||
private Set<TopicPartitionInfo> getLatestPartitions() { |
|||
log.debug("getLatestPartitionsFromQueue, queue size {}", subscribeQueue.size()); |
|||
Set<TopicPartitionInfo> partitions = null; |
|||
while (!subscribeQueue.isEmpty()) { |
|||
partitions = subscribeQueue.poll(); |
|||
log.debug("polled from the queue partitions {}", partitions); |
|||
} |
|||
log.debug("getLatestPartitionsFromQueue, partitions {}", partitions); |
|||
return partitions; |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,86 @@ |
|||
/** |
|||
* Copyright © 2016-2022 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.service.apiusage; |
|||
|
|||
import org.junit.Before; |
|||
import org.junit.Test; |
|||
import org.junit.runner.RunWith; |
|||
import org.mockito.Mock; |
|||
import org.mockito.Mockito; |
|||
import org.mockito.junit.MockitoJUnitRunner; |
|||
import org.thingsboard.rule.engine.api.MailService; |
|||
import org.thingsboard.server.cluster.TbClusterService; |
|||
import org.thingsboard.server.common.data.ApiUsageState; |
|||
import org.thingsboard.server.common.data.id.TenantId; |
|||
import org.thingsboard.server.common.msg.queue.ServiceType; |
|||
import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; |
|||
import org.thingsboard.server.dao.tenant.TbTenantProfileCache; |
|||
import org.thingsboard.server.dao.tenant.TenantService; |
|||
import org.thingsboard.server.dao.timeseries.TimeseriesService; |
|||
import org.thingsboard.server.dao.usagerecord.ApiUsageStateService; |
|||
import org.thingsboard.server.queue.discovery.PartitionService; |
|||
import org.thingsboard.server.queue.scheduler.SchedulerComponent; |
|||
import org.thingsboard.server.service.executors.DbCallbackExecutorService; |
|||
|
|||
import java.util.UUID; |
|||
|
|||
import static org.hamcrest.CoreMatchers.is; |
|||
import static org.hamcrest.MatcherAssert.assertThat; |
|||
import static org.mockito.BDDMockito.willReturn; |
|||
import static org.mockito.Mockito.never; |
|||
import static org.mockito.Mockito.spy; |
|||
import static org.mockito.Mockito.times; |
|||
|
|||
@RunWith(MockitoJUnitRunner.class) |
|||
public class DefaultTbApiUsageStateServiceTest { |
|||
|
|||
@Mock |
|||
TenantService tenantService; |
|||
@Mock |
|||
TimeseriesService tsService; |
|||
@Mock |
|||
TbClusterService clusterService; |
|||
@Mock |
|||
PartitionService partitionService; |
|||
@Mock |
|||
TenantApiUsageState tenantUsageStateMock; |
|||
@Mock |
|||
ApiUsageStateService apiUsageStateService; |
|||
@Mock |
|||
TbTenantProfileCache tenantProfileCache; |
|||
@Mock |
|||
MailService mailService; |
|||
@Mock |
|||
DbCallbackExecutorService dbExecutor; |
|||
|
|||
TenantId tenantId = TenantId.fromUUID(UUID.fromString("00797a3b-7aeb-4b5b-b57a-c2a810d0f112")); |
|||
|
|||
DefaultTbApiUsageStateService service; |
|||
|
|||
@Before |
|||
public void setUp() { |
|||
service = spy(new DefaultTbApiUsageStateService(clusterService, partitionService, tenantService, tsService, apiUsageStateService, tenantProfileCache, mailService, dbExecutor)); |
|||
} |
|||
|
|||
@Test |
|||
public void givenTenantIdFromEntityStatesMap_whenGetApiUsageState() { |
|||
service.myUsageStates.put(tenantId, tenantUsageStateMock); |
|||
ApiUsageState tenantUsageState = service.getApiUsageState(tenantId); |
|||
assertThat(tenantUsageState, is(tenantUsageStateMock.getApiUsageState())); |
|||
Mockito.verify(service, never()).getOrFetchState(tenantId, tenantId); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,235 @@ |
|||
/** |
|||
* Copyright © 2016-2022 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.transport.lwm2m.client; |
|||
|
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.eclipse.leshan.client.resource.BaseInstanceEnabler; |
|||
import org.eclipse.leshan.client.resource.LwM2mInstanceEnabler; |
|||
import org.eclipse.leshan.client.servers.ServerIdentity; |
|||
import org.eclipse.leshan.core.model.ObjectModel; |
|||
import org.eclipse.leshan.core.model.ResourceModel.Type; |
|||
import org.eclipse.leshan.core.node.LwM2mResource; |
|||
import org.eclipse.leshan.core.request.BindingMode; |
|||
import org.eclipse.leshan.core.response.ExecuteResponse; |
|||
import org.eclipse.leshan.core.response.ReadResponse; |
|||
import org.eclipse.leshan.core.response.WriteResponse; |
|||
import org.slf4j.Logger; |
|||
import org.slf4j.LoggerFactory; |
|||
|
|||
import java.util.Arrays; |
|||
import java.util.EnumSet; |
|||
import java.util.List; |
|||
import java.util.Objects; |
|||
|
|||
/** |
|||
* A simple {@link LwM2mInstanceEnabler} for the Server (1) object. |
|||
*/ |
|||
@Slf4j |
|||
public class Lwm2mServer extends BaseInstanceEnabler { |
|||
|
|||
private static final Logger LOG = LoggerFactory.getLogger(Lwm2mServer.class); |
|||
|
|||
private final static List<Integer> supportedResources = Arrays.asList(0, 1, 2, 3, 6, 7, 8, 22); |
|||
|
|||
private int shortServerId; |
|||
private long lifetime; |
|||
private Long defaultMinPeriod; |
|||
private Long defaultMaxPeriod; |
|||
private EnumSet<BindingMode> binding; |
|||
private BindingMode preferredTransport; |
|||
private boolean notifyWhenDisable; |
|||
|
|||
public Lwm2mServer() { |
|||
// should only be used at bootstrap time
|
|||
} |
|||
|
|||
public Lwm2mServer(int shortServerId, long lifetime, EnumSet<BindingMode> binding, boolean notifyWhenDisable, |
|||
BindingMode preferredTransport) { |
|||
this.shortServerId = shortServerId; |
|||
this.lifetime = lifetime; |
|||
this.binding = binding; |
|||
this.notifyWhenDisable = notifyWhenDisable; |
|||
this.preferredTransport = preferredTransport; |
|||
} |
|||
|
|||
public Lwm2mServer(int shortServerId, long lifetime) { |
|||
this(shortServerId, lifetime, EnumSet.of(BindingMode.U), false, BindingMode.U); |
|||
} |
|||
|
|||
@Override |
|||
public ReadResponse read(ServerIdentity identity, int resourceid) { |
|||
if (!identity.isSystem()) |
|||
LOG.debug("Read on Server resource /{}/{}/{}", getModel().id, getId(), resourceid); |
|||
|
|||
switch (resourceid) { |
|||
case 0: // short server ID
|
|||
return ReadResponse.success(resourceid, shortServerId); |
|||
|
|||
case 1: // lifetime
|
|||
return ReadResponse.success(resourceid, lifetime); |
|||
|
|||
case 2: // default min period
|
|||
if (null == defaultMinPeriod) |
|||
return ReadResponse.notFound(); |
|||
return ReadResponse.success(resourceid, defaultMinPeriod); |
|||
|
|||
case 3: // default max period
|
|||
if (null == defaultMaxPeriod) |
|||
return ReadResponse.notFound(); |
|||
return ReadResponse.success(resourceid, defaultMaxPeriod); |
|||
|
|||
case 6: // notification storing when disable or offline
|
|||
return ReadResponse.success(resourceid, notifyWhenDisable); |
|||
|
|||
case 7: // binding
|
|||
return ReadResponse.success(resourceid, BindingMode.toString(binding)); |
|||
|
|||
case 22: // preferred transport
|
|||
if (preferredTransport == null) |
|||
return ReadResponse.notFound(); |
|||
return ReadResponse.success(resourceid, preferredTransport.toString()); |
|||
|
|||
default: |
|||
return super.read(identity, resourceid); |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
public WriteResponse write(ServerIdentity identity, boolean replace, int resourceid, LwM2mResource value) { |
|||
if (!identity.isSystem()) |
|||
log.debug("Write on Server resource /{}/{}/{}", getModel().id, getId(), resourceid); |
|||
|
|||
switch (resourceid) { |
|||
case 0: |
|||
if (value.getType() != Type.INTEGER) { |
|||
return WriteResponse.badRequest("invalid type"); |
|||
} |
|||
int previousShortServerId = shortServerId; |
|||
shortServerId = ((Long) value.getValue()).intValue(); |
|||
if (previousShortServerId != shortServerId) |
|||
fireResourceChange(resourceid); |
|||
return WriteResponse.success(); |
|||
|
|||
case 1: |
|||
if (value.getType() != Type.INTEGER) { |
|||
return WriteResponse.badRequest("invalid type"); |
|||
} |
|||
long previousLifetime = lifetime; |
|||
lifetime = (Long) value.getValue(); |
|||
if (previousLifetime != lifetime) |
|||
fireResourceChange(resourceid); |
|||
return WriteResponse.success(); |
|||
|
|||
case 2: |
|||
if (value.getType() != Type.INTEGER) { |
|||
return WriteResponse.badRequest("invalid type"); |
|||
} |
|||
Long previousDefaultMinPeriod = defaultMinPeriod; |
|||
defaultMinPeriod = (Long) value.getValue(); |
|||
if (!Objects.equals(previousDefaultMinPeriod, defaultMinPeriod)) |
|||
fireResourceChange(resourceid); |
|||
return WriteResponse.success(); |
|||
|
|||
case 3: |
|||
if (value.getType() != Type.INTEGER) { |
|||
return WriteResponse.badRequest("invalid type"); |
|||
} |
|||
Long previousDefaultMaxPeriod = defaultMaxPeriod; |
|||
defaultMaxPeriod = (Long) value.getValue(); |
|||
if (!Objects.equals(previousDefaultMaxPeriod, defaultMaxPeriod)) |
|||
fireResourceChange(resourceid); |
|||
return WriteResponse.success(); |
|||
|
|||
case 6: // notification storing when disable or offline
|
|||
if (value.getType() != Type.BOOLEAN) { |
|||
return WriteResponse.badRequest("invalid type"); |
|||
} |
|||
boolean previousNotifyWhenDisable = notifyWhenDisable; |
|||
notifyWhenDisable = (boolean) value.getValue(); |
|||
if (previousNotifyWhenDisable != notifyWhenDisable) |
|||
fireResourceChange(resourceid); |
|||
return WriteResponse.success(); |
|||
|
|||
case 7: // binding
|
|||
if (value.getType() != Type.STRING) { |
|||
return WriteResponse.badRequest("invalid type"); |
|||
} |
|||
try { |
|||
EnumSet<BindingMode> previousBinding = binding; |
|||
binding = BindingMode.parse((String) value.getValue()); |
|||
if (!Objects.equals(previousBinding, binding)) |
|||
fireResourceChange(resourceid); |
|||
return WriteResponse.success(); |
|||
} catch (IllegalArgumentException e) { |
|||
return WriteResponse.badRequest("invalid value"); |
|||
} |
|||
case 22: // preferredTransport
|
|||
if (value.getType() != Type.STRING) { |
|||
return WriteResponse.badRequest("invalid type"); |
|||
} |
|||
try { |
|||
BindingMode previousPreferedTransport = preferredTransport; |
|||
preferredTransport = BindingMode.valueOf((String) value.getValue()); |
|||
if (!Objects.equals(previousPreferedTransport, preferredTransport)) |
|||
fireResourceChange(resourceid); |
|||
return WriteResponse.success(); |
|||
} catch (IllegalArgumentException e) { |
|||
return WriteResponse.badRequest("invalid value"); |
|||
} |
|||
|
|||
default: |
|||
return super.write(identity, replace, resourceid, value); |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
public ExecuteResponse execute(ServerIdentity identity, int resourceid, String params) { |
|||
log.info("Execute on Server resource /{}/{}/{}", getModel().id, getId(), resourceid); |
|||
if (resourceid == 8) { |
|||
getLwM2mClient().triggerRegistrationUpdate(identity); |
|||
return ExecuteResponse.success(); |
|||
} else if (resourceid == 9) { |
|||
boolean success = getLwM2mClient().triggerClientInitiatedBootstrap(true); |
|||
if (success) { |
|||
return ExecuteResponse.success(); |
|||
} |
|||
else { |
|||
return ExecuteResponse.badRequest("probably no bootstrap server configured"); |
|||
} |
|||
} else { |
|||
return super.execute(identity, resourceid, params); |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
public void reset(int resourceid) { |
|||
switch (resourceid) { |
|||
case 2: |
|||
defaultMinPeriod = null; |
|||
break; |
|||
case 3: |
|||
defaultMaxPeriod = null; |
|||
break; |
|||
default: |
|||
super.reset(resourceid); |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
public List<Integer> getAvailableResourceIds(ObjectModel model) { |
|||
return supportedResources; |
|||
} |
|||
} |
|||
@ -1,299 +0,0 @@ |
|||
#!/usr/bin/env bash |
|||
|
|||
# Change working directory |
|||
cd -- "$( |
|||
dirname "${0}" |
|||
)" || exit 1 |
|||
|
|||
readonly TRUST_PATH="Trust" |
|||
readonly CA_ROOT_CERT_KEY="ca-root" |
|||
readonly CA_ROOT_ALIAS="root" |
|||
readonly CA_INTERMEDIATE_CERT_KEY_PREF="intermediate_ca" |
|||
CA_INTERMEDIATE_START=0 |
|||
CA_INTERMEDIATE_FINISH=2 |
|||
CA_INTERMEDIATE_NUMBER=${CA_INTERMEDIATE_START} |
|||
CA_INTERMEDIATE_CERT_SIGN=${CA_ROOT_CERT_KEY} |
|||
CA_LIST_CERT_FOR_CAT="" |
|||
readonly CA_TRUST_STORE_ALL_CHAIN="lwm2mtruststorechain" |
|||
readonly CA_TRUST_STORE_PWD="server_ks_password" |
|||
readonly CA_TRUST_CERT_ALIAS="root" |
|||
readonly CA_TRUST_CERT_CHAIN_JKS="lwm2mtruststorechain" |
|||
readonly CA_TRUST_STORE_CHAIN_ALIAS="trust_cert_chain_alias" |
|||
|
|||
readonly CLIENT_PATH="Client" |
|||
readonly CLIENT_JKS_FOR_TEST="lwm2mclient" |
|||
readonly CLIENT_CERT_KEY_PREF="LwX509" |
|||
readonly CLIENT_CERT_ALIAS_PREF="client_alias_" |
|||
readonly CLIENT_STORE_PWD="client_ks_password" |
|||
readonly CLIENT_HOST_NAME="thingsboard_test.io" |
|||
CLIENT_START=0 |
|||
CLIENT_FINISH=1 |
|||
CLIENT_NUMBER=${CLIENT_START} |
|||
|
|||
SERVER_HOST_NAME="localhost.localdomain" |
|||
SERVER_LOCAL_HOST_NAME="localhost" |
|||
SERVER_PUBLIC_HOST_NAMES="-" |
|||
|
|||
readonly CF_COMMANDS=" |
|||
cfssl |
|||
cfssljson |
|||
" |
|||
|
|||
if [ ! -z "$1" ]; then |
|||
CA_INTERMEDIATE_START=$1 |
|||
CA_INTERMEDIATE_NUMBER=${CA_INTERMEDIATE_START} |
|||
fi |
|||
|
|||
if [ ! -z "$2" ]; then |
|||
CA_INTERMEDIATE_FINISH=$2 |
|||
fi |
|||
|
|||
if [ ! -z "$3" ]; then |
|||
CLIENT_START=$1 |
|||
CLIENT_NUMBER=${CLIENT_START} |
|||
fi |
|||
|
|||
if [ ! -z "$4" ]; then |
|||
CLIENT_FINISH=$4 |
|||
fi |
|||
|
|||
# Change working directory |
|||
rm -rf ${TRUST_PATH} |
|||
mkdir -p ${TRUST_PATH} |
|||
rm -rf ${CLIENT_PATH} |
|||
mkdir -p ${CLIENT_PATH} |
|||
cd -- "$( |
|||
dirname "${0}" |
|||
)" || exit 1 |
|||
|
|||
|
|||
rm *.csr |
|||
rm *.p12 |
|||
rm *.json |
|||
rm *.pem |
|||
rm *.jks |
|||
|
|||
intermediate_common_name() { |
|||
echo "${CA_INTERMEDIATE_CERT_KEY_PREF}${CA_INTERMEDIATE_NUMBER}" |
|||
} |
|||
|
|||
set_list_sert_for_cat() { |
|||
local first="$1" |
|||
echo "$first ${CA_LIST_CERT_FOR_CAT}" |
|||
} |
|||
|
|||
client_common_name() { |
|||
echo "${CLIENT_CERT_KEY_PREF}$(printf "%08d" ${CLIENT_NUMBER})" |
|||
} |
|||
|
|||
client_alias_name() { |
|||
echo "${CLIENT_CERT_ALIAS_PREF}$(printf "%08d" ${CLIENT_NUMBER})" |
|||
} |
|||
|
|||
for COMMAND in ${CF_COMMANDS}; do |
|||
if ! command -v ${COMMAND} &> /dev/null; then |
|||
echo "ERROR: Missing command ${COMMAND}" >&2 |
|||
echo "Install the package from: https://pkg.cfssl.org/" >&2 |
|||
exit 1 |
|||
fi |
|||
done |
|||
|
|||
tee ./${TRUST_PATH}/ca-config.json 1> /dev/null <<-CONFIG |
|||
{ |
|||
"signing": { |
|||
"default": { |
|||
"expiry": "8760h", |
|||
"names": [ |
|||
{ |
|||
"C": "UK", |
|||
"ST": "Kyiv city", |
|||
"L": "Kyiv", |
|||
"O": "Thingsboard", |
|||
"OU": "DEVELOPER_TEST" |
|||
} |
|||
] |
|||
}, |
|||
"profiles": { |
|||
"server": { |
|||
"expiry": "43800h", |
|||
"key": { |
|||
"algo": "ecdsa", |
|||
"size": 256 |
|||
}, |
|||
"usages": [ |
|||
"signing", |
|||
"key encipherment", |
|||
"server auth" |
|||
] |
|||
}, |
|||
"client": { |
|||
"expiry": "43800h", |
|||
"key": { |
|||
"algo": "ecdsa", |
|||
"size": 256 |
|||
}, |
|||
"usages": [ |
|||
"signing", |
|||
"key encipherment", |
|||
"client auth" |
|||
] |
|||
}, |
|||
"client-server": { |
|||
"expiry": "43800h", |
|||
"key": { |
|||
"algo": "ecdsa", |
|||
"size": 256 |
|||
}, |
|||
"usages": [ |
|||
"signing", |
|||
"key encipherment", |
|||
"server auth", |
|||
"client auth" |
|||
] |
|||
} |
|||
} |
|||
} |
|||
} |
|||
CONFIG |
|||
|
|||
tee ./${TRUST_PATH}/ca-root-to-intermediate-config.json 1> /dev/null <<-CONFIG |
|||
{ |
|||
"signing": { |
|||
"default": { |
|||
"expiry": "43800h", |
|||
"ca_constraint": { |
|||
"is_ca": true, |
|||
"max_path_len": 0, |
|||
"max_path_len_zero": true |
|||
}, |
|||
"key": { |
|||
"algo": "ecdsa", |
|||
"size": 256 |
|||
}, |
|||
"usages": [ |
|||
"digital signature", |
|||
"cert sign", |
|||
"crl sign", |
|||
"signing" |
|||
] |
|||
} |
|||
} |
|||
} |
|||
CONFIG |
|||
|
|||
echo "====================================================" |
|||
echo -e "Generate the root of certificates: \n-${CA_ROOT_KEY}-key.pem (certificate key)\n-${CA_ROOT_KEY}.pem (certificate)\n-${CA_ROOT_KEY}.csr (sign request)" |
|||
echo "====================================================" |
|||
cfssl genkey \ |
|||
-initca \ |
|||
- \ |
|||
<<-CONFIG | cfssljson -bare ./${TRUST_PATH}/${CA_ROOT_CERT_KEY} |
|||
{ |
|||
"CN": "ROOT CA", |
|||
"key": { |
|||
"algo": "ecdsa", |
|||
"size": 256 |
|||
}, |
|||
"names": [ |
|||
{ |
|||
"C": "UK", |
|||
"ST": "Kyiv city", |
|||
"L": "Kyiv", |
|||
"O": "Thingsboard", |
|||
"OU": "DEVELOPER_TEST" |
|||
} |
|||
], |
|||
"ca": { |
|||
"expiry": "131400h" |
|||
} |
|||
} |
|||
CONFIG |
|||
CA_LIST_CERT_FOR_CAT=$(set_list_sert_for_cat ./${TRUST_PATH}/${CA_ROOT_CERT_KEY}.pem) |
|||
|
|||
echo "====================================================" |
|||
echo -e "Generate and Signed the intermediates of our certificates: \n-${CA_INTERMEDIATE_CERT_KEY_PREF}?-key.pem (certificate key)\n-${CA_INTERMEDIATE_CERT_KEY_PREF}?.pem (certificate)\n-${CA_INTERMEDIATE_CERT_KEY_PREF}?.csr (sign request)" |
|||
echo "====================================================" |
|||
|
|||
while [[ ${CA_INTERMEDIATE_NUMBER} -lt ${CA_INTERMEDIATE_FINISH} ]]; |
|||
do |
|||
CA_INTERMEDIATE_CERT_KEY=$(intermediate_common_name) |
|||
CA_INTERMEDIATE_NUMBER=$((${CA_INTERMEDIATE_NUMBER} + 1)) |
|||
|
|||
cfssl gencert \ |
|||
-ca ./${TRUST_PATH}/${CA_INTERMEDIATE_CERT_SIGN}.pem \ |
|||
-ca-key ./${TRUST_PATH}/${CA_INTERMEDIATE_CERT_SIGN}-key.pem \ |
|||
-config ./${TRUST_PATH}/ca-root-to-intermediate-config.json \ |
|||
-hostname "${SERVER_HOST_NAME},${SERVER_LOCAL_HOST_NAME}${SERVER_PUBLIC_HOST_NAMES:+, }${SERVER_PUBLIC_HOST_NAMES}" \ |
|||
- \ |
|||
<<-CONFIG | cfssljson -bare ./${TRUST_PATH}/${CA_INTERMEDIATE_CERT_KEY} |
|||
{ |
|||
"CN": "${CA_INTERMEDIATE_CERT_KEY}", |
|||
"names": [ |
|||
{ |
|||
"C": "UK", |
|||
"ST": "Kyiv city", |
|||
"L": "Kyiv", |
|||
"O": "Thingsboard", |
|||
"OU": "DEVELOPER_TEST" |
|||
} |
|||
] |
|||
} |
|||
CONFIG |
|||
#openssl x509 -in ${CA_INTERMEDIATE_CERT_KEY}.pem -text -noout |
|||
CA_LIST_CERT_FOR_CAT=$(set_list_sert_for_cat ./${TRUST_PATH}/${CA_INTERMEDIATE_CERT_KEY}.pem) |
|||
CA_INTERMEDIATE_CERT_SIGN=${CA_INTERMEDIATE_CERT_KEY} |
|||
done |
|||
|
|||
echo "====================================================" |
|||
echo -e "Add the CA_certificate to keystore: ${CA_TRUST_CERT_CHAIN_JKS}.jks" |
|||
echo "====================================================" |
|||
cat ${CA_LIST_CERT_FOR_CAT} > ./${TRUST_PATH}/${CA_TRUST_STORE_ALL_CHAIN}.pem |
|||
openssl pkcs12 -export -in ./${TRUST_PATH}/${CA_TRUST_STORE_ALL_CHAIN}.pem -inkey ./${TRUST_PATH}/${CA_INTERMEDIATE_CERT_KEY}-key.pem -out ./${TRUST_PATH}/${CA_INTERMEDIATE_CERT_KEY}.p12 -name ${CA_TRUST_STORE_CHAIN_ALIAS} -CAfile ./${TRUST_PATH}/${CA_INTERMEDIATE_CERT_KEY}.pem -caname ${CA_ROOT_ALIAS} -passin pass:${CA_TRUST_STORE_PWD} -passout pass:${CA_TRUST_STORE_PWD} |
|||
keytool -importkeystore -deststorepass ${CA_TRUST_STORE_PWD} -destkeypass ${CA_TRUST_STORE_PWD} -destkeystore ./${TRUST_PATH}/${CA_TRUST_CERT_CHAIN_JKS}.jks -srckeystore ./${TRUST_PATH}/${CA_INTERMEDIATE_CERT_KEY}.p12 -srcstoretype PKCS12 -srcstorepass ${CA_TRUST_STORE_PWD} -alias ${CA_TRUST_STORE_CHAIN_ALIAS} |
|||
|
|||
keytool -list -v -keystore ./${TRUST_PATH}/lwm2mtruststorechain.jks -storepass server_ks_password -storetype PKCS12 |
|||
|
|||
echo "====================================================" |
|||
echo -e "Generate and Signed the clients of our certificates: \n-${CLIENT_CERT_KEY_PREF}?-key.pem (certificate key)\n-${CLIENT_CERT_KEY_PREF}?.pem (certificate)\n-${CCLIENT_CERT_KEY_PREF}?.csr (sign request)" |
|||
echo "====================================================" |
|||
|
|||
|
|||
while [[ ${CLIENT_NUMBER} -lt ${CLIENT_FINISH} ]]; |
|||
do |
|||
CLIENT_CERT_KEY=$(client_common_name) |
|||
CLIENT_CERT_ALIAS=$(client_alias_name) |
|||
CLIENT_NUMBER=$((${CLIENT_NUMBER} + 1)) |
|||
|
|||
cfssl gencert \ |
|||
-ca ./${TRUST_PATH}/${CA_INTERMEDIATE_CERT_KEY}.pem \ |
|||
-ca-key ./${TRUST_PATH}/${CA_INTERMEDIATE_CERT_KEY}-key.pem \ |
|||
-config ./${TRUST_PATH}/ca-config.json \ |
|||
-profile client \ |
|||
-hostname "${CLIENT_HOST_NAME}" \ |
|||
- \ |
|||
<<-CONFIG | cfssljson -bare ./${CLIENT_PATH}/${CLIENT_CERT_KEY} |
|||
{ |
|||
"CN": "${CLIENT_CERT_KEY}" |
|||
} |
|||
CONFIG |
|||
|
|||
echo "====================================================" |
|||
echo -e "Add the client certificate (${CLIENT_CERT_KEY}.pem) to keystore: ${CLIENT_JKS_FOR_TEST}.jks" |
|||
echo "====================================================" |
|||
cat ./${CLIENT_PATH}/${CLIENT_CERT_KEY}.pem ${CA_LIST_CERT_FOR_CAT} > ./${CLIENT_PATH}/${CLIENT_CERT_KEY}_chain.pem |
|||
openssl pkcs12 -export -in ./${CLIENT_PATH}/${CLIENT_CERT_KEY}_chain.pem -inkey ./${CLIENT_PATH}/${CLIENT_CERT_KEY}-key.pem -out ./${CLIENT_PATH}/${CLIENT_CERT_KEY}.p12 -name ${CLIENT_CERT_ALIAS} -CAfile ./${TRUST_PATH}/${CA_INTERMEDIATE_CERT_KEY}.pem -caname ${CA_ROOT_ALIAS} -passin pass:${CLIENT_STORE_PWD} -passout pass:${CLIENT_STORE_PWD} |
|||
keytool -importkeystore -deststorepass ${CLIENT_STORE_PWD} -destkeypass ${CLIENT_STORE_PWD} -destkeystore ./${CLIENT_PATH}/${CLIENT_JKS_FOR_TEST}.jks -srckeystore ./${CLIENT_PATH}/${CLIENT_CERT_KEY}.p12 -srcstoretype PKCS12 -srcstorepass ${CLIENT_STORE_PWD} -alias ${CLIENT_CERT_ALIAS} |
|||
|
|||
done |
|||
|
|||
keytool -list -v -keystore ./${CLIENT_PATH}/lwm2mclient.jks -storepass client_ks_password -storetype PKCS12 |
|||
|
|||
rm ./${TRUST_PATH}/*.p12 |
|||
rm ./${TRUST_PATH}/*.csr |
|||
rm ./${TRUST_PATH}/*.json |
|||
rm ./${TRUST_PATH}/${CA_ROOT_CERT_KEY}* |
|||
rm ./${TRUST_PATH}/${CA_INTERMEDIATE_CERT_KEY_PREF}* |
|||
|
|||
|
|||
rm ./${CLIENT_PATH}/*.p12 2> /dev/null |
|||
rm ./${CLIENT_PATH}/*.csr 2> /dev/null |
|||
@ -1,65 +0,0 @@ |
|||
#!/usr/bin/env bash |
|||
|
|||
readonly INTERMEDIATE_START=0 |
|||
readonly INTERMEDIATE_FINISH=2 |
|||
readonly CLIENT_START=0 |
|||
readonly CLIENT_FINISH=5 |
|||
|
|||
IS_IHFO=false |
|||
IS_SERVER_CREATED_KEY=true |
|||
IS_TRUST_CLIENT_CREATED_KEY=true |
|||
|
|||
cd -- "$( |
|||
dirname "${0}" |
|||
)" || exit 1 |
|||
|
|||
Help() |
|||
{ |
|||
# Display Help |
|||
echo "Description of the script functions." |
|||
echo |
|||
echo "Syntax: scriptTemplate [-g|h|v|V]" |
|||
echo "options:" |
|||
echo "h Print this Help." |
|||
echo "v Verbose mode." |
|||
echo "V Print software version and exit." |
|||
echo |
|||
} |
|||
|
|||
if [ "$1" == "-h" ] ; then |
|||
echo -e "Usage 2: ./`basename $0` \"Information is not displayed\" : \"Keys for the server are generated\" : \"Keys for the clients and trusts are generated\"" |
|||
echo -e "Usage 1: ./`basename $0` true \"Information is displayed\" : \"Keys for the server are generated\" : \"Keys for the clients and trusts are generated\"" |
|||
echo -e "Usage 3: ./`basename $0` true false \"Information is displayed\" : \"Keys for the server are not generated\" : \"Keys for the clients and trusts are generated\"" |
|||
echo -e "Usage 4: ./`basename $0` true false false \"Information is displayed\" : \"Keys for the server are not generated\" : \"Keys for the clients and trusts are not generated\"" |
|||
echo -e "Usage 4: ./`basename $0` true true false \"Information is displayed\" : \"Keys for the server are generated\" : \"Keys for the clients and trusts are not generated\"" |
|||
echo "This Help File: ./`basename $0` -h" |
|||
exit 0 |
|||
fi |
|||
|
|||
if [ -n "$1" ]; then |
|||
IS_IHFO=$1 |
|||
fi |
|||
|
|||
if [ -n "$2" ]; then |
|||
IS_SERVER_CREATED_KEY=$2 |
|||
fi |
|||
|
|||
if [ -n "$3" ]; then |
|||
IS_TRUST_CLIENT_CREATED_KEY=$3 |
|||
fi |
|||
|
|||
if [ "$IS_IHFO" = false ] ; then |
|||
if [ "$IS_SERVER_CREATED_KEY" = true ] ; then |
|||
./lwm2m_cfssl_chain_server_for_test.sh > /dev/null 2>&1 & |
|||
fi |
|||
if [ "$IS_TRUST_CLIENT_CREATED_KEY" = true ] ; then |
|||
./lwM2M_cfssl_chain_trusts_and_clients_for_test.sh ${INTERMEDIATE_START} ${INTERMEDIATE_FINISH} ${CLIENT_START} ${CLIENT_FINISH} > /dev/null 2>&1 & |
|||
fi |
|||
else |
|||
if [ "$IS_SERVER_CREATED_KEY" = true ] ; then |
|||
./lwm2m_cfssl_chain_server_for_test.sh |
|||
fi |
|||
if [ "$IS_TRUST_CLIENT_CREATED_KEY" = true ] ; then |
|||
./lwM2M_cfssl_chain_trusts_and_clients_for_test.sh ${INTERMEDIATE_START} ${INTERMEDIATE_FINISH} ${CLIENT_START} ${CLIENT_FINISH} |
|||
fi |
|||
fi |
|||
@ -1,298 +0,0 @@ |
|||
#!/usr/bin/env bash |
|||
|
|||
# REF: https://github.com/cloudflare/cfssl |
|||
|
|||
# Change working directory |
|||
cd -- "$( |
|||
dirname "${0}" |
|||
)" || exit 1 |
|||
|
|||
readonly CA_ROOT_CERT_KEY="ca-root" |
|||
readonly CA_ROOT_ALIAS="root" |
|||
readonly CA_INTERMEDIATE_CERT_KEY_PREF="intermediate_ca" |
|||
CA_INTERMEDIATE_NUMBER=0 |
|||
CA_LIST_CERT_FOR_CAT="" |
|||
|
|||
readonly CF_COMMANDS=" |
|||
cfssl |
|||
cfssljson |
|||
" |
|||
|
|||
readonly SERVER_JKS_FOR_TEST="lwm2mserver" |
|||
readonly STORE_PASS_PWD="server_ks_password" |
|||
readonly SERVER_PATH="Server" |
|||
readonly SERVER_CERT_KEY="lwm2mserver" |
|||
readonly SERVER_CERT_CHAIN="lwm2mserver_chain" |
|||
readonly SERVER_CERT_ALIAS="server" |
|||
readonly BS_SERVER_CERT_KEY="lwm2mserverbs" |
|||
readonly BS_SERVER_CERT_CHAIN="lwm2mserverbs_chain" |
|||
readonly BS_SERVER_CERT_ALIAS="bootstrap" |
|||
|
|||
SERVER_HOST_NAME="localhost.localdomain" |
|||
SERVER_LOCAL_HOST_NAME="localhost" |
|||
SERVER_PUBLIC_HOST_NAMES="-" |
|||
|
|||
intermediate_common_name() { |
|||
echo "${CA_INTERMEDIATE_CERT_KEY_PREF}${CA_INTERMEDIATE_NUMBER}" |
|||
} |
|||
|
|||
set_list_sert_for_cat() { |
|||
local first="$1" |
|||
echo "$first ${CA_LIST_CERT_FOR_CAT}" |
|||
} |
|||
|
|||
|
|||
# Change working directory |
|||
rm -rf ${SERVER_PATH} |
|||
mkdir -p ${SERVER_PATH} |
|||
|
|||
cd -- "$( |
|||
dirname ./${SERVER_PATH} |
|||
)" || exit 1 |
|||
|
|||
|
|||
rm *.csr |
|||
rm *.p12 |
|||
rm *.json |
|||
rm *.pem |
|||
rm *.jks |
|||
|
|||
CA_INTERMEDIATE_CERT_SIGN=${CA_ROOT_CERT_KEY} |
|||
CA_INTERMEDIATE_CERT_KEY=$(intermediate_common_name) |
|||
CA_INTERMEDIATE_NUMBER=$((${CA_INTERMEDIATE_NUMBER} + 1)) |
|||
CA_LIST_CERT_FOR_CAT="" |
|||
|
|||
for COMMAND in ${CF_COMMANDS}; do |
|||
if ! command -v ${COMMAND} &> /dev/null; then |
|||
echo "ERROR: Missing command ${COMMAND}" >&2 |
|||
echo "Install the package from: https://pkg.cfssl.org/" >&2 |
|||
exit 1 |
|||
fi |
|||
done |
|||
|
|||
tee ./${SERVER_PATH}/ca-config.json 1> /dev/null <<-CONFIG |
|||
{ |
|||
"signing": { |
|||
"default": { |
|||
"expiry": "8760h", |
|||
"names": [ |
|||
{ |
|||
"C": "UK", |
|||
"ST": "Kyiv city", |
|||
"L": "Kyiv", |
|||
"O": "Thingsboard", |
|||
"OU": "DEVELOPER_TEST" |
|||
} |
|||
] |
|||
}, |
|||
"profiles": { |
|||
"server": { |
|||
"expiry": "43800h", |
|||
"key": { |
|||
"algo": "ecdsa", |
|||
"size": 256 |
|||
}, |
|||
"usages": [ |
|||
"signing", |
|||
"key encipherment", |
|||
"server auth" |
|||
] |
|||
}, |
|||
"client": { |
|||
"expiry": "43800h", |
|||
"key": { |
|||
"algo": "ecdsa", |
|||
"size": 256 |
|||
}, |
|||
"usages": [ |
|||
"signing", |
|||
"key encipherment", |
|||
"client auth" |
|||
] |
|||
}, |
|||
"client-server": { |
|||
"expiry": "43800h", |
|||
"key": { |
|||
"algo": "ecdsa", |
|||
"size": 256 |
|||
}, |
|||
"usages": [ |
|||
"signing", |
|||
"key encipherment", |
|||
"server auth", |
|||
"client auth" |
|||
] |
|||
} |
|||
} |
|||
} |
|||
} |
|||
CONFIG |
|||
|
|||
tee ./${SERVER_PATH}/ca-root-to-intermediate-config.json 1> /dev/null <<-CONFIG |
|||
{ |
|||
"signing": { |
|||
"default": { |
|||
"expiry": "43800h", |
|||
"ca_constraint": { |
|||
"is_ca": true, |
|||
"max_path_len": 0, |
|||
"max_path_len_zero": true |
|||
}, |
|||
"key": { |
|||
"algo": "ecdsa", |
|||
"size": 256 |
|||
}, |
|||
"usages": [ |
|||
"digital signature", |
|||
"cert sign", |
|||
"crl sign", |
|||
"signing" |
|||
] |
|||
} |
|||
} |
|||
} |
|||
CONFIG |
|||
|
|||
echo "====================================================" |
|||
echo -e "Generate the root of certificates: \n-${CA_ROOT_KEY}-key.pem (certificate key)\n-${CA_ROOT_KEY}.pem (certificate)\n-${CA_ROOT_KEY}.csr (sign request)" |
|||
echo "====================================================" |
|||
cfssl genkey \ |
|||
-initca \ |
|||
- \ |
|||
<<-CONFIG | cfssljson -bare ./${SERVER_PATH}/${CA_ROOT_CERT_KEY} |
|||
{ |
|||
"CN": "ROOT CA for servers", |
|||
"key": { |
|||
"algo": "ecdsa", |
|||
"size": 256 |
|||
}, |
|||
"names": [ |
|||
{ |
|||
"C": "UK", |
|||
"ST": "Kyiv city", |
|||
"L": "Kyiv", |
|||
"O": "Thingsboard", |
|||
"OU": "DEVELOPER_TEST" |
|||
} |
|||
], |
|||
"ca": { |
|||
"expiry": "131400h" |
|||
} |
|||
} |
|||
CONFIG |
|||
CA_LIST_CERT_FOR_CAT=$(set_list_sert_for_cat ./${SERVER_PATH}/${CA_ROOT_CERT_KEY}.pem) |
|||
|
|||
echo "====================================================" |
|||
echo -e "Generate and Signed the first intermediates of our certificates: \n-${CA_INTERMEDIATE_CERT_KEY}-key.pem (certificate key)\n-${CA_INTERMEDIATE_CERT_KEY}.pem (certificate)\n-${CA_INTERMEDIATE_CERT_KEY}.csr (sign request)" |
|||
echo "====================================================" |
|||
cfssl gencert \ |
|||
-ca ./${SERVER_PATH}/${CA_INTERMEDIATE_CERT_SIGN}.pem \ |
|||
-ca-key ./${SERVER_PATH}/${CA_INTERMEDIATE_CERT_SIGN}-key.pem \ |
|||
-config ./${SERVER_PATH}/ca-root-to-intermediate-config.json \ |
|||
-hostname "${SERVER_HOST_NAME},${SERVER_LOCAL_HOST_NAME}${SERVER_PUBLIC_HOST_NAMES:+, }${SERVER_PUBLIC_HOST_NAMES}" \ |
|||
- \ |
|||
<<-CONFIG | cfssljson -bare ./${SERVER_PATH}/${CA_INTERMEDIATE_CERT_KEY} |
|||
{ |
|||
"CN": "${CA_INTERMEDIATE_CERT_KEY}", |
|||
"names": [ |
|||
{ |
|||
"C": "UK", |
|||
"ST": "Kyiv city", |
|||
"L": "Kyiv", |
|||
"O": "Thingsboard", |
|||
"OU": "DEVELOPER_TEST" |
|||
} |
|||
] |
|||
} |
|||
CONFIG |
|||
CA_LIST_CERT_FOR_CAT=$(set_list_sert_for_cat ./${SERVER_PATH}/${CA_INTERMEDIATE_CERT_KEY}.pem) |
|||
|
|||
|
|||
## Lwm2m Server certificate |
|||
echo "====================================================" |
|||
echo -e "Generate and Signed the server certificate: \n-${SERVER_CERT_KEY}-key.pem (certificate key)\n-${SERVER_CERT_KEY}.pem (certificate)\n-${SERVER_CERT_KEY}.csr (sign request)" |
|||
echo "====================================================" |
|||
cfssl gencert \ |
|||
-ca ./${SERVER_PATH}/${CA_INTERMEDIATE_CERT_KEY}.pem \ |
|||
-ca-key ./${SERVER_PATH}/${CA_INTERMEDIATE_CERT_KEY}-key.pem \ |
|||
-config ./${SERVER_PATH}/ca-config.json \ |
|||
-profile server \ |
|||
-hostname "${SERVER_HOST_NAME},${SERVER_LOCAL_HOST_NAME}${SERVER_PUBLIC_HOST_NAMES:+, }${SERVER_PUBLIC_HOST_NAMES}" \ |
|||
- \ |
|||
<<-CONFIG | cfssljson -bare ./${SERVER_PATH}/${SERVER_CERT_KEY} |
|||
{ |
|||
"CN": "${SERVER_LOCAL_HOST_NAME}" |
|||
} |
|||
CONFIG |
|||
|
|||
echo "====================================================" |
|||
echo -e "Add the server certificate (${SERVER_CERT_KEY}.pem) to keystore: ${SERVER_JKS_FOR_TEST}.jks" |
|||
echo "====================================================" |
|||
cat ./${SERVER_PATH}/${SERVER_CERT_KEY}.pem ${CA_LIST_CERT_FOR_CAT} > ./${SERVER_PATH}/${SERVER_CERT_CHAIN}.pem |
|||
openssl pkcs12 -export -in ./${SERVER_PATH}/${SERVER_CERT_CHAIN}.pem -inkey ./${SERVER_PATH}/${SERVER_CERT_KEY}-key.pem -out ./${SERVER_PATH}/${SERVER_CERT_KEY}.p12 -name ${SERVER_CERT_ALIAS} -CAfile ./${SERVER_PATH}/${CA_INTERMEDIATE_CERT_KEY}.pem -caname ${CA_ROOT_ALIAS} -passin pass:${STORE_PASS_PWD} -passout pass:${STORE_PASS_PWD} |
|||
keytool -importkeystore -deststorepass ${STORE_PASS_PWD} -destkeypass ${STORE_PASS_PWD} -destkeystore ./${SERVER_PATH}/${SERVER_JKS_FOR_TEST}.jks -srckeystore ./${SERVER_PATH}/${SERVER_CERT_KEY}.p12 -srcstoretype PKCS12 -srcstorepass ${STORE_PASS_PWD} -alias ${SERVER_CERT_ALIAS} |
|||
|
|||
|
|||
CA_INTERMEDIATE_CERT_SIGN=${CA_INTERMEDIATE_CERT_KEY} |
|||
CA_INTERMEDIATE_CERT_KEY=$(intermediate_common_name) |
|||
CA_INTERMEDIATE_NUMBER=$((${CA_INTERMEDIATE_NUMBER} + 1)) |
|||
echo "====================================================" |
|||
echo -e "Generate and Signed the second intermediates of our certificates: \n-${CA_INTERMEDIATE_CERT_KEY}-key.pem (certificate key)\n-${CA_INTERMEDIATE_CERT_KEY}.pem (certificate)\n-${CA_INTERMEDIATE_CERT_KEY}.csr (sign request)" |
|||
echo "====================================================" |
|||
cfssl gencert \ |
|||
-ca ./${SERVER_PATH}/${CA_INTERMEDIATE_CERT_SIGN}.pem \ |
|||
-ca-key ./${SERVER_PATH}/${CA_INTERMEDIATE_CERT_SIGN}-key.pem \ |
|||
-config ./${SERVER_PATH}/ca-root-to-intermediate-config.json \ |
|||
-hostname "${SERVER_HOST_NAME},${SERVER_LOCAL_HOST_NAME}${SERVER_PUBLIC_HOST_NAMES:+, }${SERVER_PUBLIC_HOST_NAMES}" \ |
|||
- \ |
|||
<<-CONFIG | cfssljson -bare ./${SERVER_PATH}/${CA_INTERMEDIATE_CERT_KEY} |
|||
{ |
|||
"CN": "${CA_INTERMEDIATE_CERT_KEY}", |
|||
"names": [ |
|||
{ |
|||
"C": "UK", |
|||
"ST": "Kyiv city", |
|||
"L": "Kyiv", |
|||
"O": "Thingsboard", |
|||
"OU": "DEVELOPER_TEST" |
|||
} |
|||
] |
|||
} |
|||
CONFIG |
|||
CA_LIST_CERT_FOR_CAT=$(set_list_sert_for_cat ./${SERVER_PATH}/${CA_INTERMEDIATE_CERT_KEY}.pem) |
|||
|
|||
## Bootstrap server certificate |
|||
echo "====================================================" |
|||
echo -e "Generate and Signed the server certificate: \n-${BS_SERVER_CERT_KEY}-key.pem (certificate key)\n-${BS_SERVER_CERT_KEY}.pem (certificate)\n-${BS_SERVER_CERT_KEY}.csr (sign request)" |
|||
echo "====================================================" |
|||
cfssl gencert \ |
|||
-ca ./${SERVER_PATH}/${CA_INTERMEDIATE_CERT_KEY}.pem \ |
|||
-ca-key ./${SERVER_PATH}/${CA_INTERMEDIATE_CERT_KEY}-key.pem \ |
|||
-config ./${SERVER_PATH}/ca-config.json \ |
|||
-profile server \ |
|||
-hostname "${SERVER_HOST_NAME},${SERVER_LOCAL_HOST_NAME}${SERVER_PUBLIC_HOST_NAMES:+, }${SERVER_PUBLIC_HOST_NAMES}" \ |
|||
- \ |
|||
<<-CONFIG | cfssljson -bare ./${SERVER_PATH}/${BS_SERVER_CERT_KEY} |
|||
{ |
|||
"CN": "${SERVER_LOCAL_HOST_NAME}" |
|||
} |
|||
CONFIG |
|||
|
|||
echo "====================================================" |
|||
echo -e "Add the Bootstrap server certificate (${BS_SERVER_CERT_KEY}.pem) to keystore: ${SERVER_JKS_FOR_TEST}.jks" |
|||
echo "====================================================" |
|||
cat ./${SERVER_PATH}/${BS_SERVER_CERT_KEY}.pem ${CA_LIST_CERT_FOR_CAT} > ./${SERVER_PATH}/${BS_SERVER_CERT_CHAIN}.pem |
|||
openssl pkcs12 -export -in ./${SERVER_PATH}/${BS_SERVER_CERT_CHAIN}.pem -inkey ./${SERVER_PATH}/${BS_SERVER_CERT_KEY}-key.pem -out ./${SERVER_PATH}/${BS_SERVER_CERT_KEY}.p12 -name ${BS_SERVER_CERT_ALIAS} -CAfile ./${SERVER_PATH}/${CA_INTERMEDIATE_CERT_KEY}.pem -caname ${CA_ROOT_ALIAS} -passin pass:${STORE_PASS_PWD} -passout pass:${STORE_PASS_PWD} |
|||
keytool -importkeystore -deststorepass ${STORE_PASS_PWD} -destkeypass ${STORE_PASS_PWD} -destkeystore ./${SERVER_PATH}/${SERVER_JKS_FOR_TEST}.jks -srckeystore ./${SERVER_PATH}/${BS_SERVER_CERT_KEY}.p12 -srcstoretype PKCS12 -srcstorepass ${STORE_PASS_PWD} -alias ${BS_SERVER_CERT_ALIAS} |
|||
|
|||
|
|||
keytool -list -v -keystore ./${SERVER_PATH}/lwm2mserver.jks -storepass server_ks_password -storetype PKCS12 |
|||
|
|||
rm ./${SERVER_PATH}/*.p12 2> /dev/null |
|||
rm ./${SERVER_PATH}/*.csr 2> /dev/null |
|||
rm ./${SERVER_PATH}/*.json 2> /dev/null |
|||
rm ./${SERVER_PATH}/${CA_INTERMEDIATE_CERT_KEY_PREF}* 2> /dev/null |
|||
rm ./${SERVER_PATH}/${CA_ROOT_CERT_KEY}* 2> /dev/null |
|||
mv ./${SERVER_PATH}/${SERVER_CERT_KEY}-key.pem ./${SERVER_PATH}/${SERVER_CERT_KEY}_key.pem |
|||
mv ./${SERVER_PATH}/${BS_SERVER_CERT_KEY}-key.pem ./${SERVER_PATH}/${BS_SERVER_CERT_KEY}_key.pem |
|||
|
|||
@ -0,0 +1,56 @@ |
|||
/** |
|||
* Copyright © 2016-2022 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.common.msg; |
|||
|
|||
import com.fasterxml.jackson.core.type.TypeReference; |
|||
import com.fasterxml.jackson.databind.JsonNode; |
|||
import com.fasterxml.jackson.databind.ObjectMapper; |
|||
import org.junit.Before; |
|||
import org.junit.Test; |
|||
|
|||
import java.util.Map; |
|||
|
|||
import static org.junit.Assert.assertEquals; |
|||
|
|||
public class TbMsgMetaDataTest { |
|||
|
|||
private static final ObjectMapper objectMapper = new ObjectMapper(); |
|||
private final String metadataJsonStr = "{\"deviceName\":\"Test Device\",\"deviceType\":\"default\",\"ts\":\"1645112691407\"}"; |
|||
private JsonNode metadataJson; |
|||
private Map<String, String> metadataExpected; |
|||
|
|||
@Before |
|||
public void startInit() throws Exception { |
|||
metadataJson = objectMapper.readValue(metadataJsonStr, JsonNode.class); |
|||
metadataExpected = objectMapper.convertValue(metadataJson, new TypeReference<>() { |
|||
}); |
|||
} |
|||
|
|||
@Test |
|||
public void testScript_whenMetadataWithoutPropertiesValueNull_returnMetadataWithAllValue() { |
|||
TbMsgMetaData tbMsgMetaData = new TbMsgMetaData(metadataExpected); |
|||
Map<String, String> dataActual = tbMsgMetaData.values(); |
|||
assertEquals(metadataExpected.size(), dataActual.size()); |
|||
} |
|||
|
|||
@Test |
|||
public void testScript_whenMetadataWithPropertiesValueNull_returnMetadataWithoutPropertiesValueEqualsNull() { |
|||
metadataExpected.put("deviceName", null); |
|||
TbMsgMetaData tbMsgMetaData = new TbMsgMetaData(metadataExpected); |
|||
Map<String, String> dataActual = tbMsgMetaData.copy().getData(); |
|||
assertEquals(metadataExpected.size() - 1, dataActual.size()); |
|||
} |
|||
} |
|||
@ -1,30 +0,0 @@ |
|||
/** |
|||
* Copyright © 2016-2022 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.transport.lwm2m.server.client; |
|||
|
|||
import org.eclipse.leshan.core.model.ResourceModel; |
|||
import org.eclipse.leshan.core.node.LwM2mResourceInstance; |
|||
|
|||
import java.io.Serializable; |
|||
|
|||
public class TbLwM2MResourceInstance extends LwM2mResourceInstance implements Serializable { |
|||
|
|||
private static final long serialVersionUID = -8322290426892538345L; |
|||
|
|||
protected TbLwM2MResourceInstance(int id, Object value, ResourceModel.Type type) { |
|||
super(id, value, type); |
|||
} |
|||
} |
|||
@ -1,30 +0,0 @@ |
|||
/** |
|||
* Copyright © 2016-2022 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.transport.lwm2m.server.client; |
|||
|
|||
import org.eclipse.leshan.core.model.ResourceModel; |
|||
import org.eclipse.leshan.core.node.LwM2mSingleResource; |
|||
|
|||
import java.io.Serializable; |
|||
|
|||
public class TbLwM2MSingleResource extends LwM2mSingleResource implements TbLwM2MResource, Serializable { |
|||
|
|||
private static final long serialVersionUID = -878078368245340809L; |
|||
|
|||
public TbLwM2MSingleResource(int id, Object value, ResourceModel.Type type) { |
|||
super(id, value, type); |
|||
} |
|||
} |
|||
@ -1,30 +0,0 @@ |
|||
/** |
|||
* Copyright © 2016-2022 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.transport.lwm2m.server.client; |
|||
|
|||
import org.eclipse.leshan.core.model.ResourceModel; |
|||
import org.eclipse.leshan.core.node.LwM2mMultipleResource; |
|||
|
|||
import java.io.Serializable; |
|||
|
|||
public class TbLwM2mMultipleResource extends LwM2mMultipleResource implements TbLwM2MResource, Serializable { |
|||
|
|||
private static final long serialVersionUID = 4658477128628087186L; |
|||
|
|||
public TbLwM2mMultipleResource(int id, ResourceModel.Type type, TbLwM2MResourceInstance... instances) { |
|||
super(id, type, instances); |
|||
} |
|||
} |
|||
@ -1,29 +0,0 @@ |
|||
/** |
|||
* Copyright © 2016-2022 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.transport.lwm2m.server.client; |
|||
|
|||
import org.eclipse.leshan.core.model.ResourceModel; |
|||
|
|||
import java.io.Serializable; |
|||
|
|||
public class TbResourceModel extends ResourceModel implements Serializable { |
|||
|
|||
private static final long serialVersionUID = -2082846558899793932L; |
|||
|
|||
public TbResourceModel(Integer id, String name, Operations operations, Boolean multiple, Boolean mandatory, Type type, String rangeEnumeration, String units, String description) { |
|||
super(id, name, operations, multiple, mandatory, type, rangeEnumeration, units, description); |
|||
} |
|||
} |
|||
@ -0,0 +1,349 @@ |
|||
/** |
|||
* Copyright © 2016-2022 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.transport.lwm2m.server.store.util; |
|||
|
|||
import com.eclipsesource.json.Json; |
|||
import com.eclipsesource.json.JsonObject; |
|||
import com.eclipsesource.json.JsonValue; |
|||
import com.google.protobuf.util.JsonFormat; |
|||
import lombok.SneakyThrows; |
|||
import org.eclipse.leshan.core.model.ResourceModel; |
|||
import org.eclipse.leshan.core.node.LwM2mMultipleResource; |
|||
import org.eclipse.leshan.core.node.LwM2mNodeException; |
|||
import org.eclipse.leshan.core.node.LwM2mResource; |
|||
import org.eclipse.leshan.core.node.LwM2mSingleResource; |
|||
import org.eclipse.leshan.core.node.ObjectLink; |
|||
import org.eclipse.leshan.core.util.datatype.ULong; |
|||
import org.eclipse.leshan.server.redis.serialization.RegistrationSerDes; |
|||
import org.thingsboard.server.common.data.device.data.PowerMode; |
|||
import org.thingsboard.server.common.data.id.TenantId; |
|||
import org.thingsboard.server.gen.transport.TransportProtos; |
|||
import org.thingsboard.server.transport.lwm2m.server.client.LwM2MClientState; |
|||
import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; |
|||
import org.thingsboard.server.transport.lwm2m.server.client.ResourceValue; |
|||
|
|||
import java.lang.reflect.Field; |
|||
import java.util.Base64; |
|||
import java.util.Date; |
|||
import java.util.HashMap; |
|||
import java.util.Map; |
|||
import java.util.UUID; |
|||
import java.util.concurrent.atomic.AtomicLong; |
|||
|
|||
public class LwM2MClientSerDes { |
|||
public static final String VALUE = "value"; |
|||
|
|||
@SneakyThrows |
|||
public static byte[] serialize(LwM2mClient client) { |
|||
JsonObject o = Json.object(); |
|||
o.add("nodeId", client.getNodeId()); |
|||
o.add("endpoint", client.getEndpoint()); |
|||
|
|||
JsonObject resources = Json.object(); |
|||
client.getResources().forEach((k, v) -> { |
|||
JsonObject resourceValue = Json.object(); |
|||
resourceValue.add("lwM2mResource", serialize(v.getLwM2mResource())); |
|||
resourceValue.add("resourceModel", serialize(v.getResourceModel())); |
|||
resources.add(k, resourceValue); |
|||
}); |
|||
o.add("resources", resources); |
|||
JsonObject sharedAttributes = Json.object(); |
|||
|
|||
for (Map.Entry<String, TransportProtos.TsKvProto> entry : client.getSharedAttributes().entrySet()) { |
|||
sharedAttributes.add(entry.getKey(), JsonFormat.printer().print(entry.getValue())); |
|||
} |
|||
|
|||
o.add("sharedAttributes", sharedAttributes); |
|||
JsonObject keyTsLatestMap = Json.object(); |
|||
client.getKeyTsLatestMap().forEach((k, v) -> { |
|||
keyTsLatestMap.add(k, v.get()); |
|||
}); |
|||
o.add("keyTsLatestMap", keyTsLatestMap); |
|||
|
|||
o.add("state", client.getState().toString()); |
|||
|
|||
if (client.getSession() != null) { |
|||
o.add("session", JsonFormat.printer().print(client.getSession())); |
|||
} |
|||
if (client.getTenantId() != null) { |
|||
o.add("tenantId", client.getTenantId().toString()); |
|||
} |
|||
if (client.getDeviceId() != null) { |
|||
o.add("deviceId", client.getDeviceId().toString()); |
|||
} |
|||
if (client.getProfileId() != null) { |
|||
o.add("profileId", client.getProfileId().toString()); |
|||
} |
|||
if (client.getPowerMode() != null) { |
|||
o.add("powerMode", client.getPowerMode().toString()); |
|||
} |
|||
if (client.getEdrxCycle() != null) { |
|||
o.add("edrxCycle", client.getEdrxCycle()); |
|||
} |
|||
if (client.getPsmActivityTimer() != null) { |
|||
o.add("psmActivityTimer", client.getPsmActivityTimer()); |
|||
} |
|||
if (client.getPagingTransmissionWindow() != null) { |
|||
o.add("pagingTransmissionWindow", client.getPagingTransmissionWindow()); |
|||
} |
|||
if (client.getRegistration() != null) { |
|||
o.add("registration", RegistrationSerDes.jSerialize(client.getRegistration())); |
|||
} |
|||
o.add("asleep", client.isAsleep()); |
|||
o.add("lastUplinkTime", client.getLastUplinkTime()); |
|||
|
|||
Field firstEdrxDownlink = LwM2mClient.class.getDeclaredField("firstEdrxDownlink"); |
|||
firstEdrxDownlink.setAccessible(true); |
|||
o.add("firstEdrxDownlink", (boolean) firstEdrxDownlink.get(client)); |
|||
o.add("retryAttempts", client.getRetryAttempts().get()); |
|||
|
|||
if (client.getLastSentRpcId() != null) { |
|||
o.add("lastSentRpcId", client.getLastSentRpcId().toString()); |
|||
} |
|||
|
|||
return o.toString().getBytes(); |
|||
} |
|||
|
|||
private static JsonObject serialize(LwM2mResource resource) { |
|||
JsonObject o = Json.object(); |
|||
o.add("id", resource.getId()); |
|||
o.add("type", resource.getType().toString()); |
|||
if (resource.isMultiInstances()) { |
|||
o.add("multiInstances", true); |
|||
JsonObject instances = Json.object(); |
|||
resource.getInstances().forEach((id, in) -> { |
|||
JsonObject instance = Json.object(); |
|||
instance.add("id", in.getId()); |
|||
addValue(instance, in.getType(), in.getValue()); |
|||
instances.add(id.toString(), instance); |
|||
}); |
|||
o.add("instances", instances); |
|||
} else { |
|||
o.add("multiInstances", false); |
|||
addValue(o, resource.getType(), resource.getValue()); |
|||
} |
|||
|
|||
return o; |
|||
} |
|||
|
|||
private static LwM2mResource parseLwM2mResource(JsonObject o) { |
|||
boolean multiInstances = o.get("multiInstances").asBoolean(); |
|||
int id = o.get("id").asInt(); |
|||
ResourceModel.Type type = ResourceModel.Type.valueOf(o.get("type").asString()); |
|||
if (multiInstances) { |
|||
Map<Integer, Object> instances = new HashMap<>(); |
|||
o.get("instances").asObject().forEach(entry -> { |
|||
instances.put(Integer.valueOf(entry.getName()), parseValue(type, entry.getValue())); |
|||
}); |
|||
return LwM2mMultipleResource.newResource(id, instances, type); |
|||
} else { |
|||
return LwM2mSingleResource.newResource(id, parseValue(type, o.get(VALUE))); |
|||
} |
|||
} |
|||
|
|||
private static Object parseValue(ResourceModel.Type type, JsonValue value) { |
|||
switch (type) { |
|||
case INTEGER: |
|||
return value.asLong(); |
|||
case FLOAT: |
|||
return value.asDouble(); |
|||
case BOOLEAN: |
|||
return value.asBoolean(); |
|||
case OPAQUE: |
|||
return Base64.getDecoder().decode(value.asString()); |
|||
case STRING: |
|||
return value.asString(); |
|||
case TIME: |
|||
return new Date(value.asLong()); |
|||
case OBJLNK: |
|||
return ObjectLink.decodeFromString(value.asString()); |
|||
case UNSIGNED_INTEGER: |
|||
return ULong.valueOf(value.asString()); |
|||
default: |
|||
throw new LwM2mNodeException(String.format("Type %s is not supported", type.name())); |
|||
} |
|||
} |
|||
|
|||
private static JsonObject serialize(ResourceModel resourceModel) { |
|||
JsonObject o = Json.object(); |
|||
o.add("id", resourceModel.id); |
|||
o.add("name", resourceModel.name); |
|||
o.add("operations", resourceModel.operations.toString()); |
|||
o.add("multiple", resourceModel.multiple); |
|||
o.add("mandatory", resourceModel.mandatory); |
|||
o.add("type", resourceModel.type.toString()); |
|||
o.add("rangeEnumeration", resourceModel.rangeEnumeration); |
|||
o.add("units", resourceModel.units); |
|||
o.add("description", resourceModel.description); |
|||
return o; |
|||
} |
|||
|
|||
private static ResourceModel parseResourceModel(JsonObject o) { |
|||
Integer id = o.get("id").asInt(); |
|||
String name = o.get("name").asString(); |
|||
ResourceModel.Operations operations = ResourceModel.Operations.valueOf(o.get("operations").asString()); |
|||
Boolean multiple = o.get("multiple").asBoolean(); |
|||
Boolean mandatory = o.get("mandatory").asBoolean(); |
|||
ResourceModel.Type type = ResourceModel.Type.valueOf(o.get("type").asString()); |
|||
String rangeEnumeration = o.get("rangeEnumeration").asString(); |
|||
String units = o.get("units").asString(); |
|||
String description = o.get("description").asString(); |
|||
return new ResourceModel(id, name, operations, multiple, mandatory, type, rangeEnumeration, units, description); |
|||
} |
|||
|
|||
private static void addValue(JsonObject o, ResourceModel.Type type, Object value) { |
|||
switch (type) { |
|||
case INTEGER: |
|||
o.add(VALUE, (Long) value); |
|||
break; |
|||
case FLOAT: |
|||
o.add(VALUE, (Double) value); |
|||
break; |
|||
case BOOLEAN: |
|||
o.add(VALUE, (Boolean) value); |
|||
break; |
|||
case OPAQUE: |
|||
o.add(VALUE, Base64.getEncoder().encodeToString((byte[]) value)); |
|||
break; |
|||
case STRING: |
|||
o.add(VALUE, (String) value); |
|||
break; |
|||
case TIME: |
|||
o.add(VALUE, ((Date) value).getTime()); |
|||
break; |
|||
case OBJLNK: |
|||
o.add(VALUE, ((ObjectLink) value).encodeToString()); |
|||
break; |
|||
case UNSIGNED_INTEGER: |
|||
o.add(VALUE, value.toString()); |
|||
break; |
|||
default: |
|||
throw new LwM2mNodeException(String.format("Type %s is not supported", type.name())); |
|||
} |
|||
} |
|||
|
|||
@SneakyThrows |
|||
public static LwM2mClient deserialize(byte[] data) { |
|||
JsonObject o = Json.parse(new String(data)).asObject(); |
|||
LwM2mClient lwM2mClient = new LwM2mClient(o.get("nodeId").asString(), o.get("endpoint").asString()); |
|||
|
|||
o.get("resources").asObject().forEach(entry -> { |
|||
JsonObject resource = entry.getValue().asObject(); |
|||
LwM2mResource lwM2mResource = parseLwM2mResource(resource.get("lwM2mResource").asObject()); |
|||
ResourceModel resourceModel = parseResourceModel(resource.get("resourceModel").asObject()); |
|||
ResourceValue resourceValue = new ResourceValue(lwM2mResource, resourceModel); |
|||
lwM2mClient.getResources().put(entry.getName(), resourceValue); |
|||
}); |
|||
|
|||
for (JsonObject.Member entry : o.get("sharedAttributes").asObject()) { |
|||
TransportProtos.TsKvProto.Builder builder = TransportProtos.TsKvProto.newBuilder(); |
|||
JsonFormat.parser().merge(entry.getValue().asString(), builder); |
|||
lwM2mClient.getSharedAttributes().put(entry.getName(), builder.build()); |
|||
} |
|||
|
|||
o.get("keyTsLatestMap").asObject().forEach(entry -> { |
|||
lwM2mClient.getKeyTsLatestMap().put(entry.getName(), new AtomicLong(entry.getValue().asLong())); |
|||
}); |
|||
|
|||
lwM2mClient.setState(LwM2MClientState.valueOf(o.get("state").asString())); |
|||
|
|||
Class<LwM2mClient> lwM2mClientClass = LwM2mClient.class; |
|||
|
|||
JsonValue session = o.get("session"); |
|||
if (session != null) { |
|||
TransportProtos.SessionInfoProto.Builder builder = TransportProtos.SessionInfoProto.newBuilder(); |
|||
JsonFormat.parser().merge(session.asString(), builder); |
|||
|
|||
Field sessionField = lwM2mClientClass.getDeclaredField("session"); |
|||
sessionField.setAccessible(true); |
|||
sessionField.set(lwM2mClient, builder.build()); |
|||
} |
|||
|
|||
JsonValue tenantId = o.get("tenantId"); |
|||
if (tenantId != null) { |
|||
Field tenantIdField = lwM2mClientClass.getDeclaredField("tenantId"); |
|||
tenantIdField.setAccessible(true); |
|||
tenantIdField.set(lwM2mClient, new TenantId(UUID.fromString(tenantId.asString()))); |
|||
} |
|||
|
|||
JsonValue deviceId = o.get("deviceId"); |
|||
if (tenantId != null) { |
|||
Field deviceIdField = lwM2mClientClass.getDeclaredField("deviceId"); |
|||
deviceIdField.setAccessible(true); |
|||
deviceIdField.set(lwM2mClient, UUID.fromString(deviceId.asString())); |
|||
} |
|||
|
|||
JsonValue profileId = o.get("profileId"); |
|||
if (tenantId != null) { |
|||
Field profileIdField = lwM2mClientClass.getDeclaredField("profileId"); |
|||
profileIdField.setAccessible(true); |
|||
profileIdField.set(lwM2mClient, UUID.fromString(profileId.asString())); |
|||
} |
|||
|
|||
JsonValue powerMode = o.get("powerMode"); |
|||
if (powerMode != null) { |
|||
Field powerModeField = lwM2mClientClass.getDeclaredField("powerMode"); |
|||
powerModeField.setAccessible(true); |
|||
powerModeField.set(lwM2mClient, PowerMode.valueOf(powerMode.asString())); |
|||
} |
|||
|
|||
JsonValue edrxCycle = o.get("edrxCycle"); |
|||
if (edrxCycle != null) { |
|||
Field edrxCycleField = lwM2mClientClass.getDeclaredField("edrxCycle"); |
|||
edrxCycleField.setAccessible(true); |
|||
edrxCycleField.set(lwM2mClient, edrxCycle.asLong()); |
|||
} |
|||
|
|||
JsonValue psmActivityTimer = o.get("psmActivityTimer"); |
|||
if (psmActivityTimer != null) { |
|||
Field psmActivityTimerField = lwM2mClientClass.getDeclaredField("psmActivityTimer"); |
|||
psmActivityTimerField.setAccessible(true); |
|||
psmActivityTimerField.set(lwM2mClient, psmActivityTimer.asLong()); |
|||
} |
|||
|
|||
JsonValue pagingTransmissionWindow = o.get("pagingTransmissionWindow"); |
|||
if (pagingTransmissionWindow != null) { |
|||
Field pagingTransmissionWindowField = lwM2mClientClass.getDeclaredField("pagingTransmissionWindow"); |
|||
pagingTransmissionWindowField.setAccessible(true); |
|||
pagingTransmissionWindowField.set(lwM2mClient, pagingTransmissionWindow.asLong()); |
|||
} |
|||
|
|||
JsonValue registration = o.get("registration"); |
|||
if (registration != null) { |
|||
lwM2mClient.setRegistration(RegistrationSerDes.deserialize(registration.asObject())); |
|||
} |
|||
|
|||
lwM2mClient.setAsleep(o.get("asleep").asBoolean()); |
|||
|
|||
Field lastUplinkTimeField = lwM2mClientClass.getDeclaredField("lastUplinkTime"); |
|||
lastUplinkTimeField.setAccessible(true); |
|||
lastUplinkTimeField.setLong(lwM2mClient, o.get("lastUplinkTime").asLong()); |
|||
|
|||
Field firstEdrxDownlinkField = lwM2mClientClass.getDeclaredField("firstEdrxDownlink"); |
|||
firstEdrxDownlinkField.setAccessible(true); |
|||
firstEdrxDownlinkField.setBoolean(lwM2mClient, o.get("firstEdrxDownlink").asBoolean()); |
|||
|
|||
lwM2mClient.getRetryAttempts().set(o.get("retryAttempts").asInt()); |
|||
|
|||
JsonValue lastSentRpcId = o.get("lastSentRpcId"); |
|||
if (lastSentRpcId != null) { |
|||
lwM2mClient.setLastSentRpcId(UUID.fromString(lastSentRpcId.asString())); |
|||
} |
|||
|
|||
return lwM2mClient; |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,38 @@ |
|||
/** |
|||
* Copyright © 2016-2022 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.transport.lwm2m.server.client; |
|||
|
|||
import org.eclipse.leshan.core.link.Link; |
|||
import org.eclipse.leshan.core.request.Identity; |
|||
import org.eclipse.leshan.server.registration.Registration; |
|||
import org.junit.Test; |
|||
import org.junit.jupiter.api.Assertions; |
|||
|
|||
import java.net.InetSocketAddress; |
|||
|
|||
public class LwM2mClientTest { |
|||
|
|||
@Test |
|||
public void setRegistration() { |
|||
LwM2mClient client = new LwM2mClient("nodeId", "testEndpoint"); |
|||
Registration registration = new Registration |
|||
.Builder("test", "testEndpoint", Identity.unsecure(new InetSocketAddress(1000))) |
|||
.objectLinks(new Link[0]) |
|||
.build(); |
|||
|
|||
Assertions.assertDoesNotThrow(() -> client.setRegistration(registration)); |
|||
} |
|||
} |
|||
@ -0,0 +1,101 @@ |
|||
/** |
|||
* Copyright © 2016-2022 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.transport.lwm2m.server.store.util; |
|||
|
|||
import org.eclipse.leshan.core.link.Link; |
|||
import org.eclipse.leshan.core.request.Identity; |
|||
import org.eclipse.leshan.server.registration.Registration; |
|||
import org.junit.Assert; |
|||
import org.junit.Test; |
|||
import org.thingsboard.server.common.data.device.data.PowerMode; |
|||
import org.thingsboard.server.common.data.id.CustomerId; |
|||
import org.thingsboard.server.common.data.id.DeviceId; |
|||
import org.thingsboard.server.common.data.id.DeviceProfileId; |
|||
import org.thingsboard.server.common.data.id.TenantId; |
|||
import org.thingsboard.server.common.transport.auth.TransportDeviceInfo; |
|||
import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse; |
|||
import org.thingsboard.server.gen.transport.TransportProtos; |
|||
import org.thingsboard.server.transport.lwm2m.server.client.LwM2MClientState; |
|||
import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; |
|||
|
|||
import java.net.InetSocketAddress; |
|||
import java.util.UUID; |
|||
|
|||
public class LwM2MClientSerDesTest { |
|||
|
|||
@Test |
|||
public void serializeDeserialize() { |
|||
LwM2mClient client = new LwM2mClient("nodeId", "testEndpoint"); |
|||
|
|||
TransportDeviceInfo tdi = new TransportDeviceInfo(); |
|||
tdi.setPowerMode(PowerMode.PSM); |
|||
tdi.setPsmActivityTimer(10000L); |
|||
tdi.setPagingTransmissionWindow(2000L); |
|||
tdi.setEdrxCycle(3000L); |
|||
tdi.setTenantId(TenantId.fromUUID(UUID.randomUUID())); |
|||
tdi.setCustomerId(new CustomerId(UUID.randomUUID())); |
|||
tdi.setDeviceId(new DeviceId(UUID.randomUUID())); |
|||
tdi.setDeviceProfileId(new DeviceProfileId(UUID.randomUUID())); |
|||
tdi.setDeviceName("testDevice"); |
|||
tdi.setDeviceType("testType"); |
|||
ValidateDeviceCredentialsResponse credentialsResponse = ValidateDeviceCredentialsResponse.builder() |
|||
.deviceInfo(tdi) |
|||
.build(); |
|||
|
|||
client.init(credentialsResponse, UUID.randomUUID()); |
|||
|
|||
Registration registration = |
|||
new Registration.Builder("test", "testEndpoint", Identity |
|||
.unsecure(new InetSocketAddress(1000))) |
|||
.supportedContentFormats() |
|||
.objectLinks(new Link[]{new Link("/")}) |
|||
.build(); |
|||
|
|||
client.setRegistration(registration); |
|||
client.setState(LwM2MClientState.REGISTERED); |
|||
|
|||
client.getSharedAttributes().put("key1", TransportProtos.TsKvProto.newBuilder().setTs(0).setKv(TransportProtos.KeyValueProto.newBuilder().setStringV("test").build()).build()); |
|||
client.getSharedAttributes().put("key2", TransportProtos.TsKvProto.newBuilder().setTs(1).setKv(TransportProtos.KeyValueProto.newBuilder().setDoubleV(1.02).build()).build()); |
|||
|
|||
byte[] bytes = LwM2MClientSerDes.serialize(client); |
|||
Assert.assertNotNull(bytes); |
|||
|
|||
LwM2mClient desClient = LwM2MClientSerDes.deserialize(bytes); |
|||
|
|||
Assert.assertEquals(client.getNodeId(), desClient.getNodeId()); |
|||
Assert.assertEquals(client.getEndpoint(), desClient.getEndpoint()); |
|||
Assert.assertEquals(client.getResources(), desClient.getResources()); |
|||
Assert.assertEquals(client.getSharedAttributes(), desClient.getSharedAttributes()); |
|||
Assert.assertEquals(client.getKeyTsLatestMap(), desClient.getKeyTsLatestMap()); |
|||
Assert.assertEquals(client.getTenantId(), desClient.getTenantId()); |
|||
Assert.assertEquals(client.getProfileId(), desClient.getProfileId()); |
|||
Assert.assertEquals(client.getDeviceId(), desClient.getDeviceId()); |
|||
Assert.assertEquals(client.getState(), desClient.getState()); |
|||
Assert.assertEquals(client.getSession(), desClient.getSession()); |
|||
Assert.assertEquals(client.getPowerMode(), desClient.getPowerMode()); |
|||
Assert.assertEquals(client.getPsmActivityTimer(), desClient.getPsmActivityTimer()); |
|||
Assert.assertEquals(client.getPagingTransmissionWindow(), desClient.getPagingTransmissionWindow()); |
|||
Assert.assertEquals(client.getEdrxCycle(), desClient.getEdrxCycle()); |
|||
Assert.assertEquals(client.getRegistration(), desClient.getRegistration()); |
|||
Assert.assertEquals(client.isAsleep(), desClient.isAsleep()); |
|||
Assert.assertEquals(client.getLastUplinkTime(), desClient.getLastUplinkTime()); |
|||
Assert.assertEquals(client.getSleepTask(), desClient.getSleepTask()); |
|||
Assert.assertEquals(client.getClientSupportContentFormats(), desClient.getClientSupportContentFormats()); |
|||
Assert.assertEquals(client.getDefaultContentFormat(), desClient.getDefaultContentFormat()); |
|||
Assert.assertEquals(client.getRetryAttempts().get(), desClient.getRetryAttempts().get()); |
|||
Assert.assertEquals(client.getLastSentRpcId(), desClient.getLastSentRpcId()); |
|||
} |
|||
} |
|||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue