124 changed files with 3825 additions and 2618 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,91 +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.dao.sql.event; |
|||
|
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.hibernate.exception.ConstraintViolationException; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.data.jpa.repository.Modifying; |
|||
import org.springframework.transaction.PlatformTransactionManager; |
|||
import org.springframework.transaction.TransactionDefinition; |
|||
import org.springframework.transaction.TransactionStatus; |
|||
import org.springframework.transaction.support.DefaultTransactionDefinition; |
|||
import org.thingsboard.server.dao.model.sql.EventEntity; |
|||
|
|||
import javax.persistence.EntityManager; |
|||
import javax.persistence.PersistenceContext; |
|||
import javax.persistence.Query; |
|||
|
|||
@Slf4j |
|||
public abstract class AbstractEventInsertRepository implements EventInsertRepository { |
|||
|
|||
@PersistenceContext |
|||
protected EntityManager entityManager; |
|||
|
|||
@Autowired |
|||
protected PlatformTransactionManager transactionManager; |
|||
|
|||
protected EventEntity saveAndGet(EventEntity entity, String insertOrUpdateOnPrimaryKeyConflict, String insertOrUpdateOnUniqueKeyConflict) { |
|||
EventEntity eventEntity = null; |
|||
TransactionStatus insertTransaction = getTransactionStatus(TransactionDefinition.PROPAGATION_REQUIRED); |
|||
try { |
|||
eventEntity = processSaveOrUpdate(entity, insertOrUpdateOnPrimaryKeyConflict); |
|||
transactionManager.commit(insertTransaction); |
|||
} catch (Throwable throwable) { |
|||
transactionManager.rollback(insertTransaction); |
|||
if (throwable.getCause() instanceof ConstraintViolationException) { |
|||
log.trace("Insert request leaded in a violation of a defined integrity constraint {} for Entity with entityId {} and entityType {}", throwable.getMessage(), entity.getEventUid(), entity.getEventType()); |
|||
TransactionStatus transaction = getTransactionStatus(TransactionDefinition.PROPAGATION_REQUIRES_NEW); |
|||
try { |
|||
eventEntity = processSaveOrUpdate(entity, insertOrUpdateOnUniqueKeyConflict); |
|||
transactionManager.commit(transaction); |
|||
} catch (Throwable th) { |
|||
log.trace("Could not execute the update statement for Entity with entityId {} and entityType {}", entity.getEventUid(), entity.getEventType()); |
|||
transactionManager.rollback(transaction); |
|||
} |
|||
} else { |
|||
log.trace("Could not execute the insert statement for Entity with entityId {} and entityType {}", entity.getEventUid(), entity.getEventType()); |
|||
} |
|||
} |
|||
return eventEntity; |
|||
} |
|||
|
|||
@Modifying |
|||
protected abstract EventEntity doProcessSaveOrUpdate(EventEntity entity, String query); |
|||
|
|||
protected Query getQuery(EventEntity entity, String query) { |
|||
return entityManager.createNativeQuery(query, EventEntity.class) |
|||
.setParameter("id", entity.getUuid()) |
|||
.setParameter("created_time", entity.getCreatedTime()) |
|||
.setParameter("body", entity.getBody().toString()) |
|||
.setParameter("entity_id", entity.getEntityId()) |
|||
.setParameter("entity_type", entity.getEntityType().name()) |
|||
.setParameter("event_type", entity.getEventType()) |
|||
.setParameter("event_uid", entity.getEventUid()) |
|||
.setParameter("tenant_id", entity.getTenantId()) |
|||
.setParameter("ts", entity.getTs()); |
|||
} |
|||
|
|||
private EventEntity processSaveOrUpdate(EventEntity entity, String query) { |
|||
return doProcessSaveOrUpdate(entity, query); |
|||
} |
|||
|
|||
private TransactionStatus getTransactionStatus(int propagationRequired) { |
|||
DefaultTransactionDefinition insertDefinition = new DefaultTransactionDefinition(); |
|||
insertDefinition.setPropagationBehavior(propagationRequired); |
|||
return transactionManager.getTransaction(insertDefinition); |
|||
} |
|||
} |
|||
@ -1,54 +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.dao.sql.event; |
|||
|
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.stereotype.Repository; |
|||
import org.thingsboard.server.dao.sql.JpaAbstractDaoListeningExecutorService; |
|||
import org.thingsboard.server.dao.util.HsqlDao; |
|||
|
|||
import java.sql.Connection; |
|||
import java.sql.PreparedStatement; |
|||
import java.sql.SQLException; |
|||
import java.util.concurrent.TimeUnit; |
|||
|
|||
@Slf4j |
|||
@HsqlDao |
|||
@Repository |
|||
public class HsqlEventCleanupRepository extends JpaAbstractDaoListeningExecutorService implements EventCleanupRepository { |
|||
|
|||
@Override |
|||
public void cleanupEvents(long otherEventsTtl, long debugEventsTtl) { |
|||
long otherExpirationTime = System.currentTimeMillis() - otherEventsTtl * 1000; |
|||
try (Connection connection = dataSource.getConnection(); |
|||
PreparedStatement stmt = connection.prepareStatement("DELETE FROM event WHERE ts < ? AND event_type != 'DEBUG_RULE_NODE' AND event_type != 'DEBUG_RULE_CHAIN'")) { |
|||
stmt.setLong(1, otherExpirationTime); |
|||
stmt.setQueryTimeout((int) TimeUnit.HOURS.toSeconds(1)); |
|||
stmt.execute(); |
|||
} catch (SQLException e) { |
|||
log.error("SQLException occurred during events TTL task execution ", e); |
|||
} |
|||
long debugExpirationTime = System.currentTimeMillis() - debugEventsTtl * 1000; |
|||
try (Connection connection = dataSource.getConnection(); |
|||
PreparedStatement stmt = connection.prepareStatement("DELETE FROM event WHERE ts < ? AND (event_type = 'DEBUG_RULE_NODE' OR event_type = 'DEBUG_RULE_CHAIN')")) { |
|||
stmt.setLong(1, debugExpirationTime); |
|||
stmt.setQueryTimeout((int) TimeUnit.HOURS.toSeconds(1)); |
|||
stmt.execute(); |
|||
} catch (SQLException e) { |
|||
log.error("SQLException occurred during events TTL task execution ", e); |
|||
} |
|||
} |
|||
} |
|||
@ -1,63 +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.dao.sql.event; |
|||
|
|||
import org.springframework.stereotype.Repository; |
|||
import org.thingsboard.server.dao.model.sql.EventEntity; |
|||
import org.thingsboard.server.dao.util.HsqlDao; |
|||
|
|||
import javax.persistence.Query; |
|||
|
|||
@HsqlDao |
|||
@Repository |
|||
public class HsqlEventInsertRepository extends AbstractEventInsertRepository { |
|||
|
|||
private static final String P_KEY_CONFLICT_STATEMENT = "(event.id=I.id)"; |
|||
private static final String UNQ_KEY_CONFLICT_STATEMENT = "(event.tenant_id=I.tenant_id AND event.entity_type=I.entity_type AND event.entity_id=I.entity_id AND event.event_type=I.event_type AND event.event_uid=I.event_uid)"; |
|||
|
|||
private static final String INSERT_OR_UPDATE_ON_P_KEY_CONFLICT = getInsertString(P_KEY_CONFLICT_STATEMENT); |
|||
private static final String INSERT_OR_UPDATE_ON_UNQ_KEY_CONFLICT = getInsertString(UNQ_KEY_CONFLICT_STATEMENT); |
|||
|
|||
@Override |
|||
public EventEntity saveOrUpdate(EventEntity entity) { |
|||
return saveAndGet(entity, INSERT_OR_UPDATE_ON_P_KEY_CONFLICT, INSERT_OR_UPDATE_ON_UNQ_KEY_CONFLICT); |
|||
} |
|||
|
|||
@Override |
|||
protected EventEntity doProcessSaveOrUpdate(EventEntity entity, String query) { |
|||
getQuery(entity, query).executeUpdate(); |
|||
return entityManager.find(EventEntity.class, entity.getUuid()); |
|||
} |
|||
|
|||
protected Query getQuery(EventEntity entity, String query) { |
|||
return entityManager.createNativeQuery(query, EventEntity.class) |
|||
.setParameter("id", entity.getUuid().toString()) |
|||
.setParameter("created_time", entity.getCreatedTime()) |
|||
.setParameter("body", entity.getBody().toString()) |
|||
.setParameter("entity_id", entity.getEntityId().toString()) |
|||
.setParameter("entity_type", entity.getEntityType().name()) |
|||
.setParameter("event_type", entity.getEventType()) |
|||
.setParameter("event_uid", entity.getEventUid()) |
|||
.setParameter("tenant_id", entity.getTenantId().toString()) |
|||
.setParameter("ts", entity.getTs()); |
|||
} |
|||
|
|||
private static String getInsertString(String conflictStatement) { |
|||
return "MERGE INTO event USING (VALUES UUID(:id), :created_time, :body, UUID(:entity_id), :entity_type, :event_type, :event_uid, UUID(:tenant_id), :ts) I (id, created_time, body, entity_id, entity_type, event_type, event_uid, tenant_id, ts) ON " + conflictStatement |
|||
+ " WHEN MATCHED THEN UPDATE SET event.id = I.id, event.created_time = I.created_time, event.body = I.body, event.entity_id = I.entity_id, event.entity_type = I.entity_type, event.event_type = I.event_type, event.event_uid = I.event_uid, event.tenant_id = I.tenant_id, event.ts = I.ts" + |
|||
" WHEN NOT MATCHED THEN INSERT (id, created_time, body, entity_id, entity_type, event_type, event_uid, tenant_id, ts) VALUES (I.id, I.created_time, I.body, I.entity_id, I.entity_type, I.event_type, I.event_uid, I.tenant_id, I.ts)"; |
|||
} |
|||
} |
|||
@ -1,53 +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.dao.sql.event; |
|||
|
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.stereotype.Repository; |
|||
import org.thingsboard.server.dao.model.sql.EventEntity; |
|||
import org.thingsboard.server.dao.util.PsqlDao; |
|||
|
|||
@Slf4j |
|||
@PsqlDao |
|||
@Repository |
|||
public class PsqlEventInsertRepository extends AbstractEventInsertRepository { |
|||
|
|||
private static final String P_KEY_CONFLICT_STATEMENT = "(id)"; |
|||
private static final String UNQ_KEY_CONFLICT_STATEMENT = "(tenant_id, created_time, entity_type, entity_id, event_type, event_uid)"; |
|||
|
|||
private static final String UPDATE_P_KEY_STATEMENT = "id = :id"; |
|||
private static final String UPDATE_UNQ_KEY_STATEMENT = "created_time = :created_time, tenant_id = :tenant_id, entity_type = :entity_type, entity_id = :entity_id, event_type = :event_type, event_uid = :event_uid"; |
|||
|
|||
private static final String INSERT_OR_UPDATE_ON_P_KEY_CONFLICT = getInsertOrUpdateString(P_KEY_CONFLICT_STATEMENT, UPDATE_UNQ_KEY_STATEMENT); |
|||
private static final String INSERT_OR_UPDATE_ON_UNQ_KEY_CONFLICT = getInsertOrUpdateString(UNQ_KEY_CONFLICT_STATEMENT, UPDATE_P_KEY_STATEMENT); |
|||
|
|||
@Override |
|||
public EventEntity saveOrUpdate(EventEntity entity) { |
|||
return saveAndGet(entity, INSERT_OR_UPDATE_ON_P_KEY_CONFLICT, INSERT_OR_UPDATE_ON_UNQ_KEY_CONFLICT); |
|||
} |
|||
|
|||
@Override |
|||
protected EventEntity doProcessSaveOrUpdate(EventEntity entity, String query) { |
|||
return (EventEntity) getQuery(entity, query).getSingleResult(); |
|||
|
|||
} |
|||
|
|||
private static String getInsertOrUpdateString(String eventKeyStatement, String updateKeyStatement) { |
|||
return "INSERT INTO event (id, created_time, body, entity_id, entity_type, event_type, event_uid, tenant_id, ts) " + |
|||
"VALUES (:id, :created_time, :body, :entity_id, :entity_type, :event_type, :event_uid, :tenant_id, :ts) " + |
|||
"ON CONFLICT " + eventKeyStatement + " DO UPDATE SET body = :body, ts = :ts," + updateKeyStatement + " returning *"; |
|||
} |
|||
} |
|||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue