466 changed files with 14205 additions and 5652 deletions
@ -0,0 +1,90 @@ |
|||||
|
-- |
||||
|
-- Copyright © 2016-2021 The Thingsboard Authors |
||||
|
-- |
||||
|
-- Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
-- you may not use this file except in compliance with the License. |
||||
|
-- You may obtain a copy of the License at |
||||
|
-- |
||||
|
-- http://www.apache.org/licenses/LICENSE-2.0 |
||||
|
-- |
||||
|
-- Unless required by applicable law or agreed to in writing, software |
||||
|
-- distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
-- See the License for the specific language governing permissions and |
||||
|
-- limitations under the License. |
||||
|
-- |
||||
|
|
||||
|
-- PROCEDURE: public.cleanup_events_by_ttl(bigint, bigint, bigint) |
||||
|
|
||||
|
DROP PROCEDURE IF EXISTS public.cleanup_events_by_ttl(bigint, bigint, bigint); |
||||
|
|
||||
|
CREATE OR REPLACE PROCEDURE public.cleanup_events_by_ttl( |
||||
|
ttl bigint, |
||||
|
debug_ttl bigint, |
||||
|
INOUT deleted bigint) |
||||
|
LANGUAGE 'plpgsql' |
||||
|
AS $BODY$ |
||||
|
DECLARE |
||||
|
ttl_ts bigint; |
||||
|
debug_ttl_ts bigint; |
||||
|
ttl_deleted_count bigint DEFAULT 0; |
||||
|
debug_ttl_deleted_count bigint DEFAULT 0; |
||||
|
BEGIN |
||||
|
IF ttl > 0 THEN |
||||
|
ttl_ts := (EXTRACT(EPOCH FROM current_timestamp) * 1000 - ttl::bigint * 1000)::bigint; |
||||
|
|
||||
|
DELETE FROM event |
||||
|
WHERE ts < ttl_ts |
||||
|
AND NOT event_type IN ('DEBUG_RULE_NODE', 'DEBUG_RULE_CHAIN', 'DEBUG_CONVERTER', 'DEBUG_INTEGRATION'); |
||||
|
|
||||
|
GET DIAGNOSTICS ttl_deleted_count = ROW_COUNT; |
||||
|
END IF; |
||||
|
|
||||
|
IF debug_ttl > 0 THEN |
||||
|
debug_ttl_ts := (EXTRACT(EPOCH FROM current_timestamp) * 1000 - debug_ttl::bigint * 1000)::bigint; |
||||
|
|
||||
|
DELETE FROM event |
||||
|
WHERE ts < debug_ttl_ts |
||||
|
AND event_type IN ('DEBUG_RULE_NODE', 'DEBUG_RULE_CHAIN', 'DEBUG_CONVERTER', 'DEBUG_INTEGRATION'); |
||||
|
|
||||
|
GET DIAGNOSTICS debug_ttl_deleted_count = ROW_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 |
||||
|
$BODY$; |
||||
|
|
||||
|
|
||||
|
-- Index: idx_event_ts |
||||
|
|
||||
|
DROP INDEX IF EXISTS public.idx_event_ts; |
||||
|
|
||||
|
-- Hint: add CONCURRENTLY to CREATE INDEX query in case of more then 1 million records or during live update |
||||
|
-- CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_event_ts |
||||
|
CREATE INDEX IF NOT EXISTS idx_event_ts |
||||
|
ON public.event |
||||
|
(ts DESC NULLS LAST) |
||||
|
WITH (FILLFACTOR=95); |
||||
|
|
||||
|
COMMENT ON INDEX public.idx_event_ts |
||||
|
IS 'This index helps to delete events by TTL using timestamp'; |
||||
|
|
||||
|
|
||||
|
-- Index: idx_event_tenant_entity_type_entity_event_type_created_time_des |
||||
|
|
||||
|
DROP INDEX IF EXISTS public.idx_event_tenant_entity_type_entity_event_type_created_time_des; |
||||
|
|
||||
|
-- CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_event_tenant_entity_type_entity_event_type_created_time_des |
||||
|
CREATE INDEX IF NOT EXISTS idx_event_tenant_entity_type_entity_event_type_created_time_des |
||||
|
ON public.event |
||||
|
(tenant_id ASC, entity_type ASC, entity_id ASC, event_type ASC, created_time DESC NULLS LAST) |
||||
|
WITH (FILLFACTOR=95); |
||||
|
|
||||
|
COMMENT ON INDEX public.idx_event_tenant_entity_type_entity_event_type_created_time_des |
||||
|
IS 'This index helps to open latest events on UI fast'; |
||||
|
|
||||
|
-- Index: idx_event_type_entity_id |
||||
|
-- Description: replaced with more suitable idx_event_tenant_entity_type_entity_event_type_created_time_des |
||||
|
DROP INDEX IF EXISTS public.idx_event_type_entity_id; |
||||
@ -0,0 +1,32 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2021 The Thingsboard Authors |
||||
|
* |
||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
* you may not use this file except in compliance with the License. |
||||
|
* You may obtain a copy of the License at |
||||
|
* |
||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
* |
||||
|
* Unless required by applicable law or agreed to in writing, software |
||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
* See the License for the specific language governing permissions and |
||||
|
* limitations under the License. |
||||
|
*/ |
||||
|
package org.thingsboard.server.service.edge.rpc; |
||||
|
|
||||
|
import com.google.common.util.concurrent.SettableFuture; |
||||
|
import lombok.Data; |
||||
|
import org.thingsboard.server.gen.edge.v1.DownlinkMsg; |
||||
|
|
||||
|
import java.util.LinkedHashMap; |
||||
|
import java.util.Map; |
||||
|
import java.util.concurrent.ScheduledFuture; |
||||
|
|
||||
|
@Data |
||||
|
public class EdgeSessionState { |
||||
|
|
||||
|
private final Map<Integer, DownlinkMsg> pendingMsgsMap = new LinkedHashMap<>(); |
||||
|
private SettableFuture<Void> sendDownlinkMsgsFuture; |
||||
|
private ScheduledFuture<?> scheduledSendDownlinkTask; |
||||
|
} |
||||
@ -0,0 +1,74 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2021 The Thingsboard Authors |
||||
|
* |
||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
* you may not use this file except in compliance with the License. |
||||
|
* You may obtain a copy of the License at |
||||
|
* |
||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
* |
||||
|
* Unless required by applicable law or agreed to in writing, software |
||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
* See the License for the specific language governing permissions and |
||||
|
* limitations under the License. |
||||
|
*/ |
||||
|
package org.thingsboard.server.service.edge.rpc; |
||||
|
|
||||
|
import org.thingsboard.server.common.data.edge.Edge; |
||||
|
import org.thingsboard.server.common.data.id.EntityId; |
||||
|
import org.thingsboard.server.service.edge.EdgeContextComponent; |
||||
|
import org.thingsboard.server.service.edge.rpc.fetch.AdminSettingsEdgeEventFetcher; |
||||
|
import org.thingsboard.server.service.edge.rpc.fetch.AssetsEdgeEventFetcher; |
||||
|
import org.thingsboard.server.service.edge.rpc.fetch.CustomerEdgeEventFetcher; |
||||
|
import org.thingsboard.server.service.edge.rpc.fetch.CustomerUsersEdgeEventFetcher; |
||||
|
import org.thingsboard.server.service.edge.rpc.fetch.DashboardsEdgeEventFetcher; |
||||
|
import org.thingsboard.server.service.edge.rpc.fetch.DeviceProfilesEdgeEventFetcher; |
||||
|
import org.thingsboard.server.service.edge.rpc.fetch.EdgeEventFetcher; |
||||
|
import org.thingsboard.server.service.edge.rpc.fetch.RuleChainsEdgeEventFetcher; |
||||
|
import org.thingsboard.server.service.edge.rpc.fetch.SystemWidgetsBundlesEdgeEventFetcher; |
||||
|
import org.thingsboard.server.service.edge.rpc.fetch.TenantAdminUsersEdgeEventFetcher; |
||||
|
import org.thingsboard.server.service.edge.rpc.fetch.TenantWidgetsBundlesEdgeEventFetcher; |
||||
|
|
||||
|
import java.util.LinkedList; |
||||
|
import java.util.List; |
||||
|
import java.util.NoSuchElementException; |
||||
|
|
||||
|
public class EdgeSyncCursor { |
||||
|
|
||||
|
List<EdgeEventFetcher> fetchers = new LinkedList<>(); |
||||
|
|
||||
|
int currentIdx = 0; |
||||
|
|
||||
|
public EdgeSyncCursor(EdgeContextComponent ctx, Edge edge) { |
||||
|
fetchers.add(new SystemWidgetsBundlesEdgeEventFetcher(ctx.getWidgetsBundleService())); |
||||
|
fetchers.add(new TenantWidgetsBundlesEdgeEventFetcher(ctx.getWidgetsBundleService())); |
||||
|
fetchers.add(new DeviceProfilesEdgeEventFetcher(ctx.getDeviceProfileService())); |
||||
|
fetchers.add(new RuleChainsEdgeEventFetcher(ctx.getRuleChainService())); |
||||
|
fetchers.add(new TenantAdminUsersEdgeEventFetcher(ctx.getUserService())); |
||||
|
if (edge.getCustomerId() != null && !EntityId.NULL_UUID.equals(edge.getCustomerId().getId())) { |
||||
|
fetchers.add(new CustomerEdgeEventFetcher()); |
||||
|
fetchers.add(new CustomerUsersEdgeEventFetcher(ctx.getUserService(), edge.getCustomerId())); |
||||
|
} |
||||
|
fetchers.add(new AdminSettingsEdgeEventFetcher(ctx.getAdminSettingsService())); |
||||
|
fetchers.add(new AssetsEdgeEventFetcher(ctx.getAssetService())); |
||||
|
fetchers.add(new DashboardsEdgeEventFetcher(ctx.getDashboardService())); |
||||
|
} |
||||
|
|
||||
|
public boolean hasNext() { |
||||
|
return fetchers.size() > currentIdx; |
||||
|
} |
||||
|
|
||||
|
public EdgeEventFetcher getNext() { |
||||
|
if (!hasNext()) { |
||||
|
throw new NoSuchElementException(); |
||||
|
} |
||||
|
EdgeEventFetcher edgeEventFetcher = fetchers.get(currentIdx); |
||||
|
currentIdx++; |
||||
|
return edgeEventFetcher; |
||||
|
} |
||||
|
|
||||
|
public int getCurrentIdx() { |
||||
|
return currentIdx; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,49 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2021 The Thingsboard Authors |
||||
|
* |
||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
* you may not use this file except in compliance with the License. |
||||
|
* You may obtain a copy of the License at |
||||
|
* |
||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
* |
||||
|
* Unless required by applicable law or agreed to in writing, software |
||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
* See the License for the specific language governing permissions and |
||||
|
* limitations under the License. |
||||
|
*/ |
||||
|
package org.thingsboard.server.service.edge.rpc.fetch; |
||||
|
|
||||
|
import lombok.AllArgsConstructor; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.thingsboard.server.common.data.edge.Edge; |
||||
|
import org.thingsboard.server.common.data.edge.EdgeEvent; |
||||
|
import org.thingsboard.server.common.data.edge.EdgeEventActionType; |
||||
|
import org.thingsboard.server.common.data.edge.EdgeEventType; |
||||
|
import org.thingsboard.server.common.data.id.TenantId; |
||||
|
import org.thingsboard.server.common.data.page.PageData; |
||||
|
import org.thingsboard.server.common.data.page.PageLink; |
||||
|
import org.thingsboard.server.service.edge.rpc.EdgeEventUtils; |
||||
|
|
||||
|
import java.util.ArrayList; |
||||
|
import java.util.List; |
||||
|
|
||||
|
@AllArgsConstructor |
||||
|
@Slf4j |
||||
|
public class CustomerEdgeEventFetcher implements EdgeEventFetcher { |
||||
|
|
||||
|
@Override |
||||
|
public PageLink getPageLink(int pageSize) { |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public PageData<EdgeEvent> fetchEdgeEvents(TenantId tenantId, Edge edge, PageLink pageLink) { |
||||
|
List<EdgeEvent> result = new ArrayList<>(); |
||||
|
result.add(EdgeEventUtils.constructEdgeEvent(edge.getTenantId(), edge.getId(), |
||||
|
EdgeEventType.CUSTOMER, EdgeEventActionType.ADDED, edge.getCustomerId(), null)); |
||||
|
// @voba - returns PageData object to be in sync with other fetchers
|
||||
|
return new PageData<>(result, 1, result.size(), false); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,33 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2021 The Thingsboard Authors |
||||
|
* |
||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
* you may not use this file except in compliance with the License. |
||||
|
* You may obtain a copy of the License at |
||||
|
* |
||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
* |
||||
|
* Unless required by applicable law or agreed to in writing, software |
||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
* See the License for the specific language governing permissions and |
||||
|
* limitations under the License. |
||||
|
*/ |
||||
|
package org.thingsboard.server.service.executors; |
||||
|
|
||||
|
import org.springframework.beans.factory.annotation.Value; |
||||
|
import org.springframework.stereotype.Component; |
||||
|
import org.thingsboard.common.util.AbstractListeningExecutor; |
||||
|
|
||||
|
@Component |
||||
|
public class GrpcCallbackExecutorService extends AbstractListeningExecutor { |
||||
|
|
||||
|
@Value("${edges.grpc_callback_thread_pool_size}") |
||||
|
private int grpcCallbackExecutorThreadPoolSize; |
||||
|
|
||||
|
@Override |
||||
|
protected int getThreadPollSize() { |
||||
|
return grpcCallbackExecutorThreadPoolSize; |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,45 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2021 The Thingsboard Authors |
||||
|
* |
||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
* you may not use this file except in compliance with the License. |
||||
|
* You may obtain a copy of the License at |
||||
|
* |
||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
* |
||||
|
* Unless required by applicable law or agreed to in writing, software |
||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
* See the License for the specific language governing permissions and |
||||
|
* limitations under the License. |
||||
|
*/ |
||||
|
package org.thingsboard.server.service.queue; |
||||
|
|
||||
|
import lombok.Builder; |
||||
|
import lombok.Data; |
||||
|
import lombok.Getter; |
||||
|
import lombok.RequiredArgsConstructor; |
||||
|
import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; |
||||
|
import org.thingsboard.server.gen.transport.TransportProtos; |
||||
|
import org.thingsboard.server.queue.TbQueueConsumer; |
||||
|
import org.thingsboard.server.queue.common.TbProtoQueueMsg; |
||||
|
|
||||
|
import java.util.Collections; |
||||
|
import java.util.Map; |
||||
|
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.locks.ReentrantLock; |
||||
|
|
||||
|
@RequiredArgsConstructor |
||||
|
@Data |
||||
|
public class TbTopicWithConsumerPerPartition { |
||||
|
private final String topic; |
||||
|
@Getter |
||||
|
private final ReentrantLock lock = new ReentrantLock(); //NonfairSync
|
||||
|
private volatile Set<TopicPartitionInfo> partitions = Collections.emptySet(); |
||||
|
private final ConcurrentMap<TopicPartitionInfo, TbQueueConsumer<TbProtoQueueMsg<TransportProtos.ToRuleEngineMsg>>> consumers = new ConcurrentHashMap<>(); |
||||
|
private final Queue<Set<TopicPartitionInfo>> subscribeQueue = new ConcurrentLinkedQueue<>(); |
||||
|
} |
||||
@ -0,0 +1,77 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2021 The Thingsboard Authors |
||||
|
* |
||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
* you may not use this file except in compliance with the License. |
||||
|
* You may obtain a copy of the License at |
||||
|
* |
||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
* |
||||
|
* Unless required by applicable law or agreed to in writing, software |
||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
* See the License for the specific language governing permissions and |
||||
|
* limitations under the License. |
||||
|
*/ |
||||
|
package org.thingsboard.server.service.rpc; |
||||
|
|
||||
|
import com.fasterxml.jackson.databind.JsonNode; |
||||
|
import lombok.RequiredArgsConstructor; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.springframework.stereotype.Service; |
||||
|
import org.thingsboard.common.util.JacksonUtil; |
||||
|
import org.thingsboard.server.common.data.id.DeviceId; |
||||
|
import org.thingsboard.server.common.data.id.RpcId; |
||||
|
import org.thingsboard.server.common.data.id.TenantId; |
||||
|
import org.thingsboard.server.common.data.page.PageData; |
||||
|
import org.thingsboard.server.common.data.page.PageLink; |
||||
|
import org.thingsboard.server.common.data.rpc.Rpc; |
||||
|
import org.thingsboard.server.common.data.rpc.RpcStatus; |
||||
|
import org.thingsboard.server.common.msg.TbMsg; |
||||
|
import org.thingsboard.server.common.msg.TbMsgMetaData; |
||||
|
import org.thingsboard.server.dao.rpc.RpcService; |
||||
|
import org.thingsboard.server.queue.util.TbCoreComponent; |
||||
|
import org.thingsboard.server.service.queue.TbClusterService; |
||||
|
|
||||
|
@TbCoreComponent |
||||
|
@Service |
||||
|
@RequiredArgsConstructor |
||||
|
@Slf4j |
||||
|
public class TbRpcService { |
||||
|
private final RpcService rpcService; |
||||
|
private final TbClusterService tbClusterService; |
||||
|
|
||||
|
public Rpc save(TenantId tenantId, Rpc rpc) { |
||||
|
Rpc saved = rpcService.save(rpc); |
||||
|
pushRpcMsgToRuleEngine(tenantId, saved); |
||||
|
return saved; |
||||
|
} |
||||
|
|
||||
|
public void save(TenantId tenantId, RpcId rpcId, RpcStatus newStatus, JsonNode response) { |
||||
|
Rpc foundRpc = rpcService.findById(tenantId, rpcId); |
||||
|
if (foundRpc != null) { |
||||
|
foundRpc.setStatus(newStatus); |
||||
|
if (response != null) { |
||||
|
foundRpc.setResponse(response); |
||||
|
} |
||||
|
Rpc saved = rpcService.save(foundRpc); |
||||
|
pushRpcMsgToRuleEngine(tenantId, saved); |
||||
|
} else { |
||||
|
log.warn("[{}] Failed to update RPC status because RPC was already deleted", rpcId); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void pushRpcMsgToRuleEngine(TenantId tenantId, Rpc rpc) { |
||||
|
TbMsg msg = TbMsg.newMsg("RPC_" + rpc.getStatus().name(), rpc.getDeviceId(), TbMsgMetaData.EMPTY, JacksonUtil.toString(rpc)); |
||||
|
tbClusterService.pushMsgToRuleEngine(tenantId, rpc.getId(), msg, null); |
||||
|
} |
||||
|
|
||||
|
public Rpc findRpcById(TenantId tenantId, RpcId rpcId) { |
||||
|
return rpcService.findById(tenantId, rpcId); |
||||
|
} |
||||
|
|
||||
|
public PageData<Rpc> findAllByDeviceIdAndStatus(TenantId tenantId, DeviceId deviceId, RpcStatus rpcStatus, PageLink pageLink) { |
||||
|
return rpcService.findAllByDeviceIdAndStatus(tenantId, deviceId, rpcStatus, pageLink); |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,83 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2021 The Thingsboard Authors |
||||
|
* |
||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
* you may not use this file except in compliance with the License. |
||||
|
* You may obtain a copy of the License at |
||||
|
* |
||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
* |
||||
|
* Unless required by applicable law or agreed to in writing, software |
||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
* See the License for the specific language governing permissions and |
||||
|
* limitations under the License. |
||||
|
*/ |
||||
|
package org.thingsboard.server.service.ttl.rpc; |
||||
|
|
||||
|
import lombok.RequiredArgsConstructor; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.springframework.beans.factory.annotation.Value; |
||||
|
import org.springframework.scheduling.annotation.Scheduled; |
||||
|
import org.springframework.stereotype.Service; |
||||
|
import org.thingsboard.server.common.data.id.TenantId; |
||||
|
import org.thingsboard.server.common.data.page.PageData; |
||||
|
import org.thingsboard.server.common.data.page.PageLink; |
||||
|
import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; |
||||
|
import org.thingsboard.server.common.msg.queue.ServiceType; |
||||
|
import org.thingsboard.server.dao.rpc.RpcDao; |
||||
|
import org.thingsboard.server.dao.tenant.TbTenantProfileCache; |
||||
|
import org.thingsboard.server.dao.tenant.TenantDao; |
||||
|
import org.thingsboard.server.queue.discovery.PartitionService; |
||||
|
import org.thingsboard.server.queue.util.TbCoreComponent; |
||||
|
|
||||
|
import java.util.Date; |
||||
|
import java.util.Optional; |
||||
|
import java.util.concurrent.TimeUnit; |
||||
|
|
||||
|
@TbCoreComponent |
||||
|
@Service |
||||
|
@Slf4j |
||||
|
@RequiredArgsConstructor |
||||
|
public class RpcCleanUpService { |
||||
|
@Value("${sql.ttl.rpc.enabled}") |
||||
|
private boolean ttlTaskExecutionEnabled; |
||||
|
|
||||
|
private final TenantDao tenantDao; |
||||
|
private final PartitionService partitionService; |
||||
|
private final TbTenantProfileCache tenantProfileCache; |
||||
|
private final RpcDao rpcDao; |
||||
|
|
||||
|
@Scheduled(initialDelayString = "#{T(org.apache.commons.lang3.RandomUtils).nextLong(0, ${sql.ttl.rpc.checking_interval})}", fixedDelayString = "${sql.ttl.rpc.checking_interval}") |
||||
|
public void cleanUp() { |
||||
|
if (ttlTaskExecutionEnabled) { |
||||
|
PageLink tenantsBatchRequest = new PageLink(10_000, 0); |
||||
|
PageData<TenantId> tenantsIds; |
||||
|
do { |
||||
|
tenantsIds = tenantDao.findTenantsIds(tenantsBatchRequest); |
||||
|
for (TenantId tenantId : tenantsIds.getData()) { |
||||
|
if (!partitionService.resolve(ServiceType.TB_CORE, tenantId, tenantId).isMyPartition()) { |
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
Optional<DefaultTenantProfileConfiguration> tenantProfileConfiguration = tenantProfileCache.get(tenantId).getProfileConfiguration(); |
||||
|
if (tenantProfileConfiguration.isEmpty() || tenantProfileConfiguration.get().getRpcTtlDays() == 0) { |
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
long ttl = TimeUnit.DAYS.toMillis(tenantProfileConfiguration.get().getRpcTtlDays()); |
||||
|
long expirationTime = System.currentTimeMillis() - ttl; |
||||
|
|
||||
|
long totalRemoved = rpcDao.deleteOutdatedRpcByTenantId(tenantId, expirationTime); |
||||
|
|
||||
|
if (totalRemoved > 0) { |
||||
|
log.info("Removed {} outdated rpc(s) for tenant {} older than {}", totalRemoved, tenantId, new Date(expirationTime)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
tenantsBatchRequest = tenantsBatchRequest.nextPageLink(); |
||||
|
} while (tenantsIds.hasNext()); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -1,44 +0,0 @@ |
|||||
/** |
|
||||
* Copyright © 2016-2021 The Thingsboard Authors |
|
||||
* |
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
||||
* you may not use this file except in compliance with the License. |
|
||||
* You may obtain a copy of the License at |
|
||||
* |
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||
* |
|
||||
* Unless required by applicable law or agreed to in writing, software |
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
||||
* See the License for the specific language governing permissions and |
|
||||
* limitations under the License. |
|
||||
*/ |
|
||||
package org.thingsboard.server.service.ttl.timeseries; |
|
||||
|
|
||||
import lombok.extern.slf4j.Slf4j; |
|
||||
import org.springframework.beans.factory.annotation.Value; |
|
||||
import org.springframework.stereotype.Service; |
|
||||
import org.thingsboard.server.dao.model.ModelConstants; |
|
||||
import org.thingsboard.server.dao.util.PsqlDao; |
|
||||
import org.thingsboard.server.dao.util.SqlTsDao; |
|
||||
|
|
||||
import java.sql.Connection; |
|
||||
import java.sql.SQLException; |
|
||||
|
|
||||
@SqlTsDao |
|
||||
@PsqlDao |
|
||||
@Service |
|
||||
@Slf4j |
|
||||
public class PsqlTimeseriesCleanUpService extends AbstractTimeseriesCleanUpService { |
|
||||
|
|
||||
@Value("${sql.postgres.ts_key_value_partitioning}") |
|
||||
private String partitionType; |
|
||||
|
|
||||
@Override |
|
||||
protected void doCleanUp(Connection connection) throws SQLException { |
|
||||
long totalPartitionsRemoved = executeQuery(connection, "call drop_partitions_by_max_ttl('" + partitionType + "'," + systemTtl + ", 0);"); |
|
||||
log.info("Total partitions removed by TTL: [{}]", totalPartitionsRemoved); |
|
||||
long totalEntitiesTelemetryRemoved = executeQuery(connection, "call cleanup_timeseries_by_ttl('" + ModelConstants.NULL_UUID + "'," + systemTtl + ", 0);"); |
|
||||
log.info("Total telemetry removed stats by TTL for entities: [{}]", totalEntitiesTelemetryRemoved); |
|
||||
} |
|
||||
} |
|
||||
@ -1,36 +0,0 @@ |
|||||
/** |
|
||||
* Copyright © 2016-2021 The Thingsboard Authors |
|
||||
* |
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
||||
* you may not use this file except in compliance with the License. |
|
||||
* You may obtain a copy of the License at |
|
||||
* |
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||
* |
|
||||
* Unless required by applicable law or agreed to in writing, software |
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
||||
* See the License for the specific language governing permissions and |
|
||||
* limitations under the License. |
|
||||
*/ |
|
||||
package org.thingsboard.server.service.ttl.timeseries; |
|
||||
|
|
||||
import lombok.extern.slf4j.Slf4j; |
|
||||
import org.springframework.stereotype.Service; |
|
||||
import org.thingsboard.server.dao.model.ModelConstants; |
|
||||
import org.thingsboard.server.dao.util.TimescaleDBTsDao; |
|
||||
|
|
||||
import java.sql.Connection; |
|
||||
import java.sql.SQLException; |
|
||||
|
|
||||
@TimescaleDBTsDao |
|
||||
@Service |
|
||||
@Slf4j |
|
||||
public class TimescaleTimeseriesCleanUpService extends AbstractTimeseriesCleanUpService { |
|
||||
|
|
||||
@Override |
|
||||
protected void doCleanUp(Connection connection) throws SQLException { |
|
||||
long totalEntitiesTelemetryRemoved = executeQuery(connection, "call cleanup_timeseries_by_ttl('" + ModelConstants.NULL_UUID + "'," + systemTtl + ", 0);"); |
|
||||
log.info("Total telemetry removed stats by TTL for entities: [{}]", totalEntitiesTelemetryRemoved); |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,58 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2021 The Thingsboard Authors |
||||
|
* |
||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
* you may not use this file except in compliance with the License. |
||||
|
* You may obtain a copy of the License at |
||||
|
* |
||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
* |
||||
|
* Unless required by applicable law or agreed to in writing, software |
||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
* See the License for the specific language governing permissions and |
||||
|
* limitations under the License. |
||||
|
*/ |
||||
|
package org.thingsboard.server.actors.device; |
||||
|
|
||||
|
import org.junit.Before; |
||||
|
import org.junit.Test; |
||||
|
import org.thingsboard.common.util.LinkedHashMapRemoveEldest; |
||||
|
import org.thingsboard.server.actors.ActorSystemContext; |
||||
|
import org.thingsboard.server.common.data.id.DeviceId; |
||||
|
import org.thingsboard.server.common.data.id.TenantId; |
||||
|
import org.thingsboard.server.dao.device.DeviceService; |
||||
|
|
||||
|
import static org.hamcrest.CoreMatchers.instanceOf; |
||||
|
import static org.hamcrest.CoreMatchers.is; |
||||
|
import static org.hamcrest.CoreMatchers.notNullValue; |
||||
|
import static org.hamcrest.MatcherAssert.assertThat; |
||||
|
import static org.mockito.BDDMockito.willReturn; |
||||
|
import static org.mockito.Mockito.mock; |
||||
|
|
||||
|
public class DeviceActorMessageProcessorTest { |
||||
|
|
||||
|
public static final long MAX_CONCURRENT_SESSIONS_PER_DEVICE = 10L; |
||||
|
ActorSystemContext systemContext; |
||||
|
DeviceService deviceService; |
||||
|
TenantId tenantId = TenantId.SYS_TENANT_ID; |
||||
|
DeviceId deviceId = DeviceId.fromString("78bf9b26-74ef-4af2-9cfb-ad6cf24ad2ec"); |
||||
|
|
||||
|
DeviceActorMessageProcessor processor; |
||||
|
|
||||
|
@Before |
||||
|
public void setUp() { |
||||
|
systemContext = mock(ActorSystemContext.class); |
||||
|
deviceService = mock(DeviceService.class); |
||||
|
willReturn(MAX_CONCURRENT_SESSIONS_PER_DEVICE).given(systemContext).getMaxConcurrentSessionsPerDevice(); |
||||
|
willReturn(deviceService).given(systemContext).getDeviceService(); |
||||
|
processor = new DeviceActorMessageProcessor(systemContext, tenantId, deviceId); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void givenSystemContext_whenNewInstance_thenVerifySessionMapMaxSize() { |
||||
|
assertThat(processor.sessions, instanceOf(LinkedHashMapRemoveEldest.class)); |
||||
|
assertThat(processor.sessions.getMaxEntries(), is(MAX_CONCURRENT_SESSIONS_PER_DEVICE)); |
||||
|
assertThat(processor.sessions.getRemovalConsumer(), notNullValue()); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,54 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2021 The Thingsboard Authors |
||||
|
* |
||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
* you may not use this file except in compliance with the License. |
||||
|
* You may obtain a copy of the License at |
||||
|
* |
||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
* |
||||
|
* Unless required by applicable law or agreed to in writing, software |
||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
* See the License for the specific language governing permissions and |
||||
|
* limitations under the License. |
||||
|
*/ |
||||
|
package org.thingsboard.server.service.install; |
||||
|
|
||||
|
import org.junit.Test; |
||||
|
|
||||
|
import static org.mockito.ArgumentMatchers.anyString; |
||||
|
import static org.mockito.BDDMockito.willDoNothing; |
||||
|
import static org.mockito.Mockito.spy; |
||||
|
import static org.mockito.Mockito.times; |
||||
|
import static org.mockito.Mockito.verify; |
||||
|
|
||||
|
public class PsqlEntityDatabaseSchemaServiceTest { |
||||
|
|
||||
|
@Test |
||||
|
public void givenPsqlDbSchemaService_whenCreateDatabaseSchema_thenVerifyPsqlIndexSpecificCall() throws Exception { |
||||
|
PsqlEntityDatabaseSchemaService service = spy(new PsqlEntityDatabaseSchemaService()); |
||||
|
willDoNothing().given(service).executeQueryFromFile(anyString()); |
||||
|
|
||||
|
service.createDatabaseSchema(); |
||||
|
|
||||
|
verify(service, times(1)).createDatabaseIndexes(); |
||||
|
verify(service, times(1)).executeQueryFromFile(PsqlEntityDatabaseSchemaService.SCHEMA_ENTITIES_SQL); |
||||
|
verify(service, times(1)).executeQueryFromFile(PsqlEntityDatabaseSchemaService.SCHEMA_ENTITIES_IDX_SQL); |
||||
|
verify(service, times(1)).executeQueryFromFile(PsqlEntityDatabaseSchemaService.SCHEMA_ENTITIES_IDX_PSQL_ADDON_SQL); |
||||
|
verify(service, times(3)).executeQueryFromFile(anyString()); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void givenPsqlDbSchemaService_whenCreateDatabaseIndexes_thenVerifyPsqlIndexSpecificCall() throws Exception { |
||||
|
PsqlEntityDatabaseSchemaService service = spy(new PsqlEntityDatabaseSchemaService()); |
||||
|
willDoNothing().given(service).executeQueryFromFile(anyString()); |
||||
|
|
||||
|
service.createDatabaseIndexes(); |
||||
|
|
||||
|
verify(service, times(1)).executeQueryFromFile(PsqlEntityDatabaseSchemaService.SCHEMA_ENTITIES_IDX_SQL); |
||||
|
verify(service, times(1)).executeQueryFromFile(PsqlEntityDatabaseSchemaService.SCHEMA_ENTITIES_IDX_PSQL_ADDON_SQL); |
||||
|
verify(service, times(2)).executeQueryFromFile(anyString()); |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,83 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2021 The Thingsboard Authors |
||||
|
* |
||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
* you may not use this file except in compliance with the License. |
||||
|
* You may obtain a copy of the License at |
||||
|
* |
||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
* |
||||
|
* Unless required by applicable law or agreed to in writing, software |
||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
* See the License for the specific language governing permissions and |
||||
|
* limitations under the License. |
||||
|
*/ |
||||
|
package org.thingsboard.server.service.state; |
||||
|
|
||||
|
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.server.common.data.id.DeviceId; |
||||
|
import org.thingsboard.server.dao.attributes.AttributesService; |
||||
|
import org.thingsboard.server.dao.device.DeviceService; |
||||
|
import org.thingsboard.server.dao.tenant.TenantService; |
||||
|
import org.thingsboard.server.dao.timeseries.TimeseriesService; |
||||
|
import org.thingsboard.server.queue.discovery.PartitionService; |
||||
|
import org.thingsboard.server.service.queue.TbClusterService; |
||||
|
|
||||
|
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 DefaultDeviceStateServiceTest { |
||||
|
|
||||
|
@Mock |
||||
|
TenantService tenantService; |
||||
|
@Mock |
||||
|
DeviceService deviceService; |
||||
|
@Mock |
||||
|
AttributesService attributesService; |
||||
|
@Mock |
||||
|
TimeseriesService tsService; |
||||
|
@Mock |
||||
|
TbClusterService clusterService; |
||||
|
@Mock |
||||
|
PartitionService partitionService; |
||||
|
@Mock |
||||
|
DeviceStateData deviceStateDataMock; |
||||
|
|
||||
|
DeviceId deviceId = DeviceId.fromString("00797a3b-7aeb-4b5b-b57a-c2a810d0f112"); |
||||
|
|
||||
|
DefaultDeviceStateService service; |
||||
|
|
||||
|
@Before |
||||
|
public void setUp() { |
||||
|
service = spy(new DefaultDeviceStateService(tenantService, deviceService, attributesService, tsService, clusterService, partitionService)); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void givenDeviceIdFromDeviceStatesMap_whenGetOrFetchDeviceStateData_thenNoStackOverflow() { |
||||
|
service.deviceStates.put(deviceId, deviceStateDataMock); |
||||
|
DeviceStateData deviceStateData = service.getOrFetchDeviceStateData(deviceId); |
||||
|
assertThat(deviceStateData, is(deviceStateDataMock)); |
||||
|
Mockito.verify(service, never()).fetchDeviceStateData(deviceId); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void givenDeviceIdWithoutDeviceStateInMap_whenGetOrFetchDeviceStateData_thenFetchDeviceStateData() { |
||||
|
service.deviceStates.clear(); |
||||
|
willReturn(deviceStateDataMock).given(service).fetchDeviceStateData(deviceId); |
||||
|
DeviceStateData deviceStateData = service.getOrFetchDeviceStateData(deviceId); |
||||
|
assertThat(deviceStateData, is(deviceStateDataMock)); |
||||
|
Mockito.verify(service, times(1)).fetchDeviceStateData(deviceId); |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,50 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2021 The Thingsboard Authors |
||||
|
* |
||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
* you may not use this file except in compliance with the License. |
||||
|
* You may obtain a copy of the License at |
||||
|
* |
||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
* |
||||
|
* Unless required by applicable law or agreed to in writing, software |
||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
* See the License for the specific language governing permissions and |
||||
|
* limitations under the License. |
||||
|
*/ |
||||
|
package org.thingsboard.server.service.ttl; |
||||
|
|
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.junit.Test; |
||||
|
import org.junit.runner.RunWith; |
||||
|
import org.springframework.beans.factory.annotation.Value; |
||||
|
import org.springframework.boot.test.context.SpringBootTest; |
||||
|
import org.springframework.test.context.junit4.SpringRunner; |
||||
|
|
||||
|
import static org.hamcrest.MatcherAssert.assertThat; |
||||
|
import static org.hamcrest.Matchers.greaterThanOrEqualTo; |
||||
|
import static org.hamcrest.Matchers.is; |
||||
|
import static org.hamcrest.Matchers.lessThanOrEqualTo; |
||||
|
import static org.thingsboard.server.service.ttl.EventsCleanUpService.RANDOM_DELAY_INTERVAL_MS_EXPRESSION; |
||||
|
|
||||
|
@RunWith(SpringRunner.class) |
||||
|
@SpringBootTest(classes = EventsCleanUpServiceTest.class) |
||||
|
@Slf4j |
||||
|
public class EventsCleanUpServiceTest { |
||||
|
|
||||
|
@Value(RANDOM_DELAY_INTERVAL_MS_EXPRESSION) |
||||
|
long randomDelayMs; |
||||
|
@Value("${sql.ttl.events.execution_interval_ms}") |
||||
|
long executionIntervalMs; |
||||
|
|
||||
|
@Test |
||||
|
public void givenInterval_whenRandomDelay_ThenDelayInInterval() { |
||||
|
log.info("randomDelay {}", randomDelayMs); |
||||
|
log.info("executionIntervalMs {}", executionIntervalMs); |
||||
|
assertThat(executionIntervalMs, is(2220000L)); |
||||
|
assertThat(randomDelayMs, greaterThanOrEqualTo(0L)); |
||||
|
assertThat(randomDelayMs, lessThanOrEqualTo(executionIntervalMs)); |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,43 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2021 The Thingsboard Authors |
||||
|
* |
||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
* you may not use this file except in compliance with the License. |
||||
|
* You may obtain a copy of the License at |
||||
|
* |
||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
* |
||||
|
* Unless required by applicable law or agreed to in writing, software |
||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
* See the License for the specific language governing permissions and |
||||
|
* limitations under the License. |
||||
|
*/ |
||||
|
package org.thingsboard.server.transport.lwm2m; |
||||
|
|
||||
|
import org.eclipse.leshan.client.object.Security; |
||||
|
import org.eclipse.leshan.core.util.Hex; |
||||
|
import org.junit.Test; |
||||
|
import org.thingsboard.server.common.data.device.credentials.lwm2m.PSKClientCredentials; |
||||
|
|
||||
|
import java.nio.charset.StandardCharsets; |
||||
|
|
||||
|
import static org.eclipse.leshan.client.object.Security.psk; |
||||
|
|
||||
|
public class PskLwm2mIntegrationTest extends AbstractLwM2MIntegrationTest { |
||||
|
|
||||
|
@Test |
||||
|
public void testConnectWithPSKAndObserveTelemetry() throws Exception { |
||||
|
String pskIdentity = "SOME_PSK_ID"; |
||||
|
String pskKey = "73656372657450534b"; |
||||
|
PSKClientCredentials clientCredentials = new PSKClientCredentials(); |
||||
|
clientCredentials.setEndpoint(ENDPOINT); |
||||
|
clientCredentials.setKey(pskKey); |
||||
|
clientCredentials.setIdentity(pskIdentity); |
||||
|
Security security = psk(SECURE_URI, |
||||
|
123, |
||||
|
pskIdentity.getBytes(StandardCharsets.UTF_8), |
||||
|
Hex.decodeHex(pskKey.toCharArray())); |
||||
|
super.basicTestConnectionObserveTelemetry(security, clientCredentials, SECURE_COAP_CONFIG, ENDPOINT); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,40 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2021 The Thingsboard Authors |
||||
|
* |
||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
* you may not use this file except in compliance with the License. |
||||
|
* You may obtain a copy of the License at |
||||
|
* |
||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
* |
||||
|
* Unless required by applicable law or agreed to in writing, software |
||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
* See the License for the specific language governing permissions and |
||||
|
* limitations under the License. |
||||
|
*/ |
||||
|
package org.thingsboard.server.transport.lwm2m; |
||||
|
|
||||
|
import org.eclipse.leshan.client.object.Security; |
||||
|
import org.eclipse.leshan.core.util.Hex; |
||||
|
import org.junit.Test; |
||||
|
import org.thingsboard.server.common.data.device.credentials.lwm2m.RPKClientCredentials; |
||||
|
|
||||
|
import static org.eclipse.leshan.client.object.Security.rpk; |
||||
|
|
||||
|
public class RpkLwM2MIntegrationTest extends AbstractLwM2MIntegrationTest { |
||||
|
|
||||
|
@Test |
||||
|
public void testConnectWithRPKAndObserveTelemetry() throws Exception { |
||||
|
RPKClientCredentials rpkClientCredentials = new RPKClientCredentials(); |
||||
|
rpkClientCredentials.setEndpoint(ENDPOINT); |
||||
|
rpkClientCredentials.setKey(Hex.encodeHexString(clientPublicKey.getEncoded())); |
||||
|
Security security = rpk(SECURE_URI, |
||||
|
123, |
||||
|
clientPublicKey.getEncoded(), |
||||
|
clientPrivateKey.getEncoded(), |
||||
|
serverX509Cert.getPublicKey().getEncoded()); |
||||
|
super.basicTestConnectionObserveTelemetry(security, rpkClientCredentials, SECURE_COAP_CONFIG, ENDPOINT); |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,155 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2021 The Thingsboard Authors |
||||
|
* |
||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
* you may not use this file except in compliance with the License. |
||||
|
* You may obtain a copy of the License at |
||||
|
* |
||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
* |
||||
|
* Unless required by applicable law or agreed to in writing, software |
||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
* See the License for the specific language governing permissions and |
||||
|
* limitations under the License. |
||||
|
*/ |
||||
|
package org.thingsboard.server.transport.lwm2m.client; |
||||
|
|
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.eclipse.leshan.client.resource.BaseInstanceEnabler; |
||||
|
import org.eclipse.leshan.client.servers.ServerIdentity; |
||||
|
import org.eclipse.leshan.core.model.ObjectModel; |
||||
|
import org.eclipse.leshan.core.node.LwM2mResource; |
||||
|
import org.eclipse.leshan.core.response.ExecuteResponse; |
||||
|
import org.eclipse.leshan.core.response.ReadResponse; |
||||
|
import org.eclipse.leshan.core.response.WriteResponse; |
||||
|
|
||||
|
import javax.security.auth.Destroyable; |
||||
|
import java.util.Arrays; |
||||
|
import java.util.List; |
||||
|
import java.util.concurrent.Executors; |
||||
|
import java.util.concurrent.ScheduledExecutorService; |
||||
|
import java.util.concurrent.TimeUnit; |
||||
|
import java.util.concurrent.atomic.AtomicInteger; |
||||
|
|
||||
|
@Slf4j |
||||
|
public class FwLwM2MDevice extends BaseInstanceEnabler implements Destroyable { |
||||
|
|
||||
|
private static final List<Integer> supportedResources = Arrays.asList(0, 1, 2, 3, 5, 6, 7, 9); |
||||
|
|
||||
|
private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(); |
||||
|
|
||||
|
private final AtomicInteger state = new AtomicInteger(0); |
||||
|
|
||||
|
private final AtomicInteger updateResult = new AtomicInteger(0); |
||||
|
|
||||
|
@Override |
||||
|
public ReadResponse read(ServerIdentity identity, int resourceId) { |
||||
|
if (!identity.isSystem()) |
||||
|
log.info("Read on Device resource /{}/{}/{}", getModel().id, getId(), resourceId); |
||||
|
switch (resourceId) { |
||||
|
case 3: |
||||
|
return ReadResponse.success(resourceId, getState()); |
||||
|
case 5: |
||||
|
return ReadResponse.success(resourceId, getUpdateResult()); |
||||
|
case 6: |
||||
|
return ReadResponse.success(resourceId, getPkgName()); |
||||
|
case 7: |
||||
|
return ReadResponse.success(resourceId, getPkgVersion()); |
||||
|
case 9: |
||||
|
return ReadResponse.success(resourceId, getFirmwareUpdateDeliveryMethod()); |
||||
|
default: |
||||
|
return super.read(identity, resourceId); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public ExecuteResponse execute(ServerIdentity identity, int resourceId, String params) { |
||||
|
String withParams = null; |
||||
|
if (params != null && params.length() != 0) { |
||||
|
withParams = " with params " + params; |
||||
|
} |
||||
|
log.info("Execute on Device resource /{}/{}/{} {}", getModel().id, getId(), resourceId, withParams != null ? withParams : ""); |
||||
|
|
||||
|
switch (resourceId) { |
||||
|
case 2: |
||||
|
startUpdating(); |
||||
|
return ExecuteResponse.success(); |
||||
|
default: |
||||
|
return super.execute(identity, resourceId, params); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public WriteResponse write(ServerIdentity identity, boolean replace, int resourceId, LwM2mResource value) { |
||||
|
log.info("Write on Device resource /{}/{}/{}", getModel().id, getId(), resourceId); |
||||
|
|
||||
|
switch (resourceId) { |
||||
|
case 0: |
||||
|
startDownloading(); |
||||
|
return WriteResponse.success(); |
||||
|
case 1: |
||||
|
startDownloading(); |
||||
|
return WriteResponse.success(); |
||||
|
default: |
||||
|
return super.write(identity, replace, resourceId, value); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private int getState() { |
||||
|
return state.get(); |
||||
|
} |
||||
|
|
||||
|
private int getUpdateResult() { |
||||
|
return updateResult.get(); |
||||
|
} |
||||
|
|
||||
|
private String getPkgName() { |
||||
|
return "firmware"; |
||||
|
} |
||||
|
|
||||
|
private String getPkgVersion() { |
||||
|
return "1.0.0"; |
||||
|
} |
||||
|
|
||||
|
private int getFirmwareUpdateDeliveryMethod() { |
||||
|
return 1; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public List<Integer> getAvailableResourceIds(ObjectModel model) { |
||||
|
return supportedResources; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void destroy() { |
||||
|
scheduler.shutdownNow(); |
||||
|
} |
||||
|
|
||||
|
private void startDownloading() { |
||||
|
scheduler.schedule(() -> { |
||||
|
try { |
||||
|
state.set(1); |
||||
|
fireResourcesChange(3); |
||||
|
Thread.sleep(100); |
||||
|
state.set(2); |
||||
|
fireResourcesChange(3); |
||||
|
} catch (Exception e) { |
||||
|
} |
||||
|
}, 100, TimeUnit.MILLISECONDS); |
||||
|
} |
||||
|
|
||||
|
private void startUpdating() { |
||||
|
scheduler.schedule(() -> { |
||||
|
try { |
||||
|
state.set(3); |
||||
|
fireResourcesChange(3); |
||||
|
Thread.sleep(100); |
||||
|
updateResult.set(1); |
||||
|
fireResourcesChange(5); |
||||
|
} catch (Exception e) { |
||||
|
} |
||||
|
}, 100, TimeUnit.MILLISECONDS); |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,155 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2021 The Thingsboard Authors |
||||
|
* |
||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
* you may not use this file except in compliance with the License. |
||||
|
* You may obtain a copy of the License at |
||||
|
* |
||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
* |
||||
|
* Unless required by applicable law or agreed to in writing, software |
||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
* See the License for the specific language governing permissions and |
||||
|
* limitations under the License. |
||||
|
*/ |
||||
|
package org.thingsboard.server.transport.lwm2m.client; |
||||
|
|
||||
|
import lombok.SneakyThrows; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.eclipse.leshan.client.resource.BaseInstanceEnabler; |
||||
|
import org.eclipse.leshan.client.servers.ServerIdentity; |
||||
|
import org.eclipse.leshan.core.model.ObjectModel; |
||||
|
import org.eclipse.leshan.core.node.LwM2mResource; |
||||
|
import org.eclipse.leshan.core.response.ExecuteResponse; |
||||
|
import org.eclipse.leshan.core.response.ReadResponse; |
||||
|
import org.eclipse.leshan.core.response.WriteResponse; |
||||
|
|
||||
|
import javax.security.auth.Destroyable; |
||||
|
import java.util.Arrays; |
||||
|
import java.util.List; |
||||
|
import java.util.concurrent.Executors; |
||||
|
import java.util.concurrent.ScheduledExecutorService; |
||||
|
import java.util.concurrent.TimeUnit; |
||||
|
import java.util.concurrent.atomic.AtomicInteger; |
||||
|
|
||||
|
@Slf4j |
||||
|
public class SwLwM2MDevice extends BaseInstanceEnabler implements Destroyable { |
||||
|
|
||||
|
private static final List<Integer> supportedResources = Arrays.asList(0, 1, 2, 3, 4, 6, 7, 9); |
||||
|
|
||||
|
private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(); |
||||
|
|
||||
|
private final AtomicInteger state = new AtomicInteger(0); |
||||
|
|
||||
|
private final AtomicInteger updateResult = new AtomicInteger(0); |
||||
|
|
||||
|
@Override |
||||
|
public ReadResponse read(ServerIdentity identity, int resourceId) { |
||||
|
if (!identity.isSystem()) |
||||
|
log.info("Read on Device resource /{}/{}/{}", getModel().id, getId(), resourceId); |
||||
|
switch (resourceId) { |
||||
|
case 0: |
||||
|
return ReadResponse.success(resourceId, getPkgName()); |
||||
|
case 1: |
||||
|
return ReadResponse.success(resourceId, getPkgVersion()); |
||||
|
case 7: |
||||
|
return ReadResponse.success(resourceId, getUpdateState()); |
||||
|
case 9: |
||||
|
return ReadResponse.success(resourceId, getUpdateResult()); |
||||
|
default: |
||||
|
return super.read(identity, resourceId); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public ExecuteResponse execute(ServerIdentity identity, int resourceId, String params) { |
||||
|
String withParams = null; |
||||
|
if (params != null && params.length() != 0) { |
||||
|
withParams = " with params " + params; |
||||
|
} |
||||
|
log.info("Execute on Device resource /{}/{}/{} {}", getModel().id, getId(), resourceId, withParams != null ? withParams : ""); |
||||
|
|
||||
|
switch (resourceId) { |
||||
|
case 4: |
||||
|
startUpdating(); |
||||
|
return ExecuteResponse.success(); |
||||
|
case 6: |
||||
|
return ExecuteResponse.success(); |
||||
|
default: |
||||
|
return super.execute(identity, resourceId, params); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public WriteResponse write(ServerIdentity identity, boolean replace, int resourceId, LwM2mResource value) { |
||||
|
log.info("Write on Device resource /{}/{}/{}", getModel().id, getId(), resourceId); |
||||
|
|
||||
|
switch (resourceId) { |
||||
|
case 2: |
||||
|
startDownloading(); |
||||
|
return WriteResponse.success(); |
||||
|
case 3: |
||||
|
startDownloading(); |
||||
|
return WriteResponse.success(); |
||||
|
default: |
||||
|
return super.write(identity, replace, resourceId, value); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private int getUpdateState() { |
||||
|
return state.get(); |
||||
|
} |
||||
|
|
||||
|
private int getUpdateResult() { |
||||
|
return updateResult.get(); |
||||
|
} |
||||
|
|
||||
|
private String getPkgName() { |
||||
|
return "software"; |
||||
|
} |
||||
|
|
||||
|
private String getPkgVersion() { |
||||
|
return "1.0.0"; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public List<Integer> getAvailableResourceIds(ObjectModel model) { |
||||
|
return supportedResources; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void destroy() { |
||||
|
scheduler.shutdownNow(); |
||||
|
} |
||||
|
|
||||
|
private void startDownloading() { |
||||
|
scheduler.schedule(() -> { |
||||
|
try { |
||||
|
state.set(1); |
||||
|
updateResult.set(1); |
||||
|
fireResourcesChange(7, 9); |
||||
|
Thread.sleep(100); |
||||
|
state.set(2); |
||||
|
fireResourcesChange(7); |
||||
|
Thread.sleep(100); |
||||
|
state.set(3); |
||||
|
fireResourcesChange(7); |
||||
|
Thread.sleep(100); |
||||
|
updateResult.set(3); |
||||
|
fireResourcesChange(9); |
||||
|
} catch (Exception e) { |
||||
|
|
||||
|
} |
||||
|
}, 100, TimeUnit.MILLISECONDS); |
||||
|
} |
||||
|
|
||||
|
private void startUpdating() { |
||||
|
scheduler.schedule(() -> { |
||||
|
state.set(4); |
||||
|
updateResult.set(2); |
||||
|
fireResourcesChange(7, 9); |
||||
|
}, 100, TimeUnit.MILLISECONDS); |
||||
|
} |
||||
|
|
||||
|
} |
||||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue