Merge branch 'master' into fix_bug_lwm2m_profile_update_dynamic

This commit is contained in:
nick 2025-01-08 16:17:56 +02:00
commit df8d9e2540
12 changed files with 53 additions and 1716 deletions

View File

@ -17,6 +17,7 @@ package org.thingsboard.server.service.ttl;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled; import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@ -66,7 +67,7 @@ public class AlarmsCleanUpService {
try { try {
cleanUp(tenantId); cleanUp(tenantId);
} catch (Exception e) { } catch (Exception e) {
log.warn("Failed to clean up alarms by ttl for tenant {}", tenantId, e); getLogger().warn("Failed to clean up alarms by ttl for tenant {}", tenantId, e);
} }
} }
} }
@ -105,8 +106,13 @@ public class AlarmsCleanUpService {
alarmService.delAlarmTypes(tenantId, typesToRemove); alarmService.delAlarmTypes(tenantId, typesToRemove);
if (totalRemoved > 0) { if (totalRemoved > 0) {
log.info("Removed {} outdated alarm(s) for tenant {} older than {}", totalRemoved, tenantId, new Date(expirationTime)); getLogger().info("Removed {} outdated alarm(s) for tenant {} older than {}", totalRemoved, tenantId, new Date(expirationTime));
} }
} }
// wrapper for tests to spy on static logger
Logger getLogger() {
return log;
}
} }

View File

@ -149,10 +149,6 @@ import org.thingsboard.server.service.security.auth.rest.LoginRequest;
import org.thingsboard.server.service.security.model.token.JwtTokenFactory; import org.thingsboard.server.service.security.model.token.JwtTokenFactory;
import java.io.IOException; import java.io.IOException;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.ArrayList; import java.util.ArrayList;
@ -1053,33 +1049,6 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest {
throw new AssertionError("Unexpected status " + mvcResult.getResponse().getStatus()); throw new AssertionError("Unexpected status " + mvcResult.getResponse().getStatus());
} }
protected static <T> T getFieldValue(Object target, String fieldName) throws Exception {
Field field = target.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
return (T) field.get(target);
}
protected static void setStaticFieldValue(Class<?> targetCls, String fieldName, Object value) throws Exception {
Field field = targetCls.getDeclaredField(fieldName);
field.setAccessible(true);
field.set(null, value);
}
protected static void setStaticFinalFieldValue(Class<?> targetCls, String fieldName, Object value) throws Exception {
Field field = targetCls.getDeclaredField(fieldName);
field.setAccessible(true);
// Get the VarHandle for the 'modifiers' field in the Field class
MethodHandles.Lookup lookup = MethodHandles.privateLookupIn(Field.class, MethodHandles.lookup());
VarHandle modifiersHandle = lookup.findVarHandle(Field.class, "modifiers", int.class);
// Remove the final modifier from the field
int currentModifiers = field.getModifiers();
modifiersHandle.set(field, currentModifiers & ~Modifier.FINAL);
// Set the new value
field.set(null, value);
}
protected int getDeviceActorSubscriptionCount(DeviceId deviceId, FeatureType featureType) { protected int getDeviceActorSubscriptionCount(DeviceId deviceId, FeatureType featureType) {
DeviceActorMessageProcessor processor = getDeviceActorProcessor(deviceId); DeviceActorMessageProcessor processor = getDeviceActorProcessor(deviceId);
Map<UUID, SessionInfo> subscriptions = (Map<UUID, SessionInfo>) ReflectionTestUtils.getField(processor, getMapName(featureType)); Map<UUID, SessionInfo> subscriptions = (Map<UUID, SessionInfo>) ReflectionTestUtils.getField(processor, getMapName(featureType));

View File

@ -16,8 +16,10 @@
package org.thingsboard.server.service.script; package org.thingsboard.server.service.script;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.script.api.ScriptType; import org.thingsboard.script.api.ScriptType;
import org.thingsboard.script.api.tbel.DefaultTbelInvokeService;
import org.thingsboard.script.api.tbel.TbelInvokeService; import org.thingsboard.script.api.tbel.TbelInvokeService;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.controller.AbstractControllerTest; import org.thingsboard.server.controller.AbstractControllerTest;
@ -28,7 +30,8 @@ import java.util.concurrent.ExecutionException;
import static org.thingsboard.server.common.data.msg.TbMsgType.POST_TELEMETRY_REQUEST; import static org.thingsboard.server.common.data.msg.TbMsgType.POST_TELEMETRY_REQUEST;
public abstract class AbstractTbelInvokeTest extends AbstractControllerTest { @SpringBootTest(classes = DefaultTbelInvokeService.class)
public abstract class AbstractTbelInvokeTest {
@Autowired @Autowired
protected TbelInvokeService invokeService; protected TbelInvokeService invokeService;

View File

@ -18,7 +18,6 @@ package org.thingsboard.server.service.script;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.script.api.tbel.TbDate; import org.thingsboard.script.api.tbel.TbDate;
import org.thingsboard.server.dao.service.DaoSqlTest;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.ArrayList; import java.util.ArrayList;
@ -34,7 +33,6 @@ import java.util.concurrent.atomic.AtomicReference;
import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
@DaoSqlTest
class TbelInvokeDocsIoTest extends AbstractTbelInvokeTest { class TbelInvokeDocsIoTest extends AbstractTbelInvokeTest {
private String decoderStr; private String decoderStr;

View File

@ -22,9 +22,9 @@ import org.junit.Ignore;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.TestPropertySource;
import org.springframework.test.util.ReflectionTestUtils;
import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.script.api.tbel.TbelScript; import org.thingsboard.script.api.tbel.TbelScript;
import org.thingsboard.server.dao.service.DaoSqlTest;
import java.io.Serializable; import java.io.Serializable;
import java.util.ArrayList; import java.util.ArrayList;
@ -38,7 +38,6 @@ import java.util.concurrent.TimeUnit;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.assertj.core.api.Assertions.assertThatThrownBy;
@DaoSqlTest
@TestPropertySource(properties = { @TestPropertySource(properties = {
"tbel.max_script_body_size=100", "tbel.max_script_body_size=100",
"tbel.max_total_args_size=50", "tbel.max_total_args_size=50",
@ -120,9 +119,9 @@ class TbelInvokeServiceTest extends AbstractTbelInvokeTest {
scriptsIds.add(scriptId); scriptsIds.add(scriptId);
} }
Map<UUID, String> scriptIdToHash = getFieldValue(invokeService, "scriptIdToHash"); Map<UUID, String> scriptIdToHash = (Map<UUID, String>) ReflectionTestUtils.getField(invokeService, "scriptIdToHash");
Map<String, TbelScript> scriptMap = getFieldValue(invokeService, "scriptMap"); Map<String, TbelScript> scriptMap = (Map<String, TbelScript>) ReflectionTestUtils.getField(invokeService, "scriptMap");
Cache<String, Serializable> compiledScriptsCache = getFieldValue(invokeService, "compiledScriptsCache"); Cache<String, Serializable> compiledScriptsCache = (Cache<String, Serializable>) ReflectionTestUtils.getField(invokeService, "compiledScriptsCache");
String scriptHash = scriptIdToHash.get(scriptsIds.get(0)); String scriptHash = scriptIdToHash.get(scriptsIds.get(0));
@ -140,9 +139,9 @@ class TbelInvokeServiceTest extends AbstractTbelInvokeTest {
scriptsIds.add(scriptId); scriptsIds.add(scriptId);
} }
Map<UUID, String> scriptIdToHash = getFieldValue(invokeService, "scriptIdToHash"); Map<UUID, String> scriptIdToHash = (Map<UUID, String>) ReflectionTestUtils.getField(invokeService, "scriptIdToHash");
Map<String, TbelScript> scriptMap = getFieldValue(invokeService, "scriptMap"); Map<String, TbelScript> scriptMap = (Map<String, TbelScript>) ReflectionTestUtils.getField(invokeService, "scriptMap");
Cache<String, Serializable> compiledScriptsCache = getFieldValue(invokeService, "compiledScriptsCache"); Cache<String, Serializable> compiledScriptsCache = (Cache<String, Serializable>) ReflectionTestUtils.getField(invokeService, "compiledScriptsCache");
String scriptHash = scriptIdToHash.get(scriptsIds.get(0)); String scriptHash = scriptIdToHash.get(scriptsIds.get(0));
for (int i = 0; i < 9; i++) { for (int i = 0; i < 9; i++) {
@ -163,8 +162,8 @@ class TbelInvokeServiceTest extends AbstractTbelInvokeTest {
@Ignore("This test is based on assumption that Caffeine cache is LRU based but in fact it is based on " + @Ignore("This test is based on assumption that Caffeine cache is LRU based but in fact it is based on " +
"Tiny LFU which is the cause that the tests fail sometime: https://arxiv.org/pdf/1512.00727.pdf") "Tiny LFU which is the cause that the tests fail sometime: https://arxiv.org/pdf/1512.00727.pdf")
public void whenCompiledScriptsCacheIsTooBig_thenRemoveRarelyUsedScripts() throws Exception { public void whenCompiledScriptsCacheIsTooBig_thenRemoveRarelyUsedScripts() throws Exception {
Map<UUID, String> scriptIdToHash = getFieldValue(invokeService, "scriptIdToHash"); Map<UUID, String> scriptIdToHash = (Map<UUID, String>) ReflectionTestUtils.getField(invokeService, "scriptIdToHash");
Cache<String, Serializable> compiledScriptsCache = getFieldValue(invokeService, "compiledScriptsCache"); Cache<String, Serializable> compiledScriptsCache = (Cache<String, Serializable>) ReflectionTestUtils.getField(invokeService, "compiledScriptsCache");
List<UUID> scriptsIds = new ArrayList<>(); List<UUID> scriptsIds = new ArrayList<>();
for (int i = 0; i < 110; i++) { // tbel.compiled_scripts_cache_size = 100 for (int i = 0; i < 110; i++) { // tbel.compiled_scripts_cache_size = 100

View File

@ -15,7 +15,7 @@
*/ */
package org.thingsboard.server.service.ttl; package org.thingsboard.server.service.ttl;
import org.junit.BeforeClass; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.mockito.Mockito; import org.mockito.Mockito;
import org.slf4j.Logger; import org.slf4j.Logger;
@ -40,6 +40,7 @@ import java.util.concurrent.TimeUnit;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.startsWith; import static org.mockito.ArgumentMatchers.startsWith;
import static org.mockito.BDDMockito.willReturn;
import static org.mockito.Mockito.never; import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
@ -49,19 +50,19 @@ import static org.mockito.Mockito.verify;
}) })
public class AlarmsCleanUpServiceTest extends AbstractControllerTest { public class AlarmsCleanUpServiceTest extends AbstractControllerTest {
@Autowired @SpyBean
private AlarmsCleanUpService alarmsCleanUpService; private AlarmsCleanUpService alarmsCleanUpService;
@SpyBean @SpyBean
private AlarmService alarmService; private AlarmService alarmService;
@Autowired @Autowired
private AlarmDao alarmDao; private AlarmDao alarmDao;
private static Logger cleanUpServiceLogger; private Logger cleanUpServiceLoggerSpy;
@BeforeClass @Before
public static void before() throws Exception { public void beforeEach() throws Exception {
cleanUpServiceLogger = Mockito.spy(LoggerFactory.getLogger(AlarmsCleanUpService.class)); cleanUpServiceLoggerSpy = Mockito.spy(LoggerFactory.getLogger(AlarmsCleanUpService.class));
setStaticFinalFieldValue(AlarmsCleanUpService.class, "log", cleanUpServiceLogger); willReturn(cleanUpServiceLoggerSpy).given(alarmsCleanUpService).getLogger();
} }
@Test @Test
@ -110,7 +111,7 @@ public class AlarmsCleanUpServiceTest extends AbstractControllerTest {
verify(alarmService, never()).delAlarm(eq(tenantId), eq(freshAlarm), eq(false)); verify(alarmService, never()).delAlarm(eq(tenantId), eq(freshAlarm), eq(false));
} }
verify(cleanUpServiceLogger).info(startsWith("Removed {} outdated alarm"), eq((long) count), eq(tenantId), any()); verify(cleanUpServiceLoggerSpy).info(startsWith("Removed {} outdated alarm"), eq((long) count), eq(tenantId), any());
} }
} }

View File

@ -16,8 +16,6 @@
export interface TbMessage { export interface TbMessage {
scriptIdMSB: string; // deprecated
scriptIdLSB: string; // deprecated
scriptHash: string; scriptHash: string;
} }

View File

@ -18,7 +18,7 @@ import config from 'config';
import { _logger } from '../config/logger'; import { _logger } from '../config/logger';
import { JsExecutor, TbScript } from './jsExecutor'; import { JsExecutor, TbScript } from './jsExecutor';
import { performance } from 'perf_hooks'; import { performance } from 'perf_hooks';
import { isString, parseJsErrorDetails, toUUIDString, UUIDFromBuffer, UUIDToBits, isNotUUID } from './utils'; import { isString, parseJsErrorDetails, UUIDFromBuffer, UUIDToBits } from './utils';
import { IQueue } from '../queue/queue.models'; import { IQueue } from '../queue/queue.models';
import { import {
JsCompileRequest, JsCompileRequest,
@ -306,26 +306,12 @@ export class JsInvokeMessageProcessor {
} }
private static createCompileResponse(scriptId: string, success: boolean, errorCode?: number, err?: any): JsCompileResponse { private static createCompileResponse(scriptId: string, success: boolean, errorCode?: number, err?: any): JsCompileResponse {
if (isNotUUID(scriptId)) {
return { return {
errorCode: errorCode, errorCode: errorCode,
success: success, success: success,
errorDetails: parseJsErrorDetails(err), errorDetails: parseJsErrorDetails(err),
scriptIdMSB: "0",
scriptIdLSB: "0",
scriptHash: scriptId scriptHash: scriptId
}; };
} else { // this is for backward compatibility (to be able to work with tb-node of previous version) - todo: remove in the next release
let scriptIdBits = UUIDToBits(scriptId);
return {
errorCode: errorCode,
success: success,
errorDetails: parseJsErrorDetails(err),
scriptIdMSB: scriptIdBits[0],
scriptIdLSB: scriptIdBits[1],
scriptHash: ""
};
}
} }
private static createInvokeResponse(result: string | undefined, success: boolean, errorCode?: number, err?: any): JsInvokeResponse { private static createInvokeResponse(result: string | undefined, success: boolean, errorCode?: number, err?: any): JsInvokeResponse {
@ -338,26 +324,14 @@ export class JsInvokeMessageProcessor {
} }
private static createReleaseResponse(scriptId: string, success: boolean): JsReleaseResponse { private static createReleaseResponse(scriptId: string, success: boolean): JsReleaseResponse {
if (isNotUUID(scriptId)) {
return { return {
success: success, success: success,
scriptIdMSB: "0",
scriptIdLSB: "0",
scriptHash: scriptId, scriptHash: scriptId,
}; };
} else { // todo: remove in the next release
let scriptIdBits = UUIDToBits(scriptId);
return {
success: success,
scriptIdMSB: scriptIdBits[0],
scriptIdLSB: scriptIdBits[1],
scriptHash: ""
}
}
} }
private static getScriptId(request: TbMessage): string { private static getScriptId(request: TbMessage): string {
return request.scriptHash ? request.scriptHash : toUUIDString(request.scriptIdMSB, request.scriptIdLSB); return request.scriptHash;
} }
private incrementUseScriptId(scriptId: string) { private incrementUseScriptId(scriptId: string) {

View File

@ -17,13 +17,6 @@
import Long from 'long'; import Long from 'long';
import uuidParse from 'uuid-parse'; import uuidParse from 'uuid-parse';
export function toUUIDString(mostSigBits: string, leastSigBits: string): string {
const msbBytes = Long.fromValue(mostSigBits, false).toBytes(false);
const lsbBytes = Long.fromValue(leastSigBits, false).toBytes(false);
const uuidBytes = msbBytes.concat(lsbBytes);
return uuidParse.unparse(uuidBytes as any);
}
export function UUIDFromBuffer(buf: Buffer): string { export function UUIDFromBuffer(buf: Buffer): string {
return uuidParse.unparse(buf); return uuidParse.unparse(buf);
} }
@ -59,10 +52,6 @@ export function parseJsErrorDetails(err: any): string | undefined {
return details; return details;
} }
export function isNotUUID(candidate: string) {
return candidate.length != 36 || !candidate.includes('-');
}
export function isNotEmptyStr(value: any): boolean { export function isNotEmptyStr(value: any): boolean {
return typeof value === 'string' && value.trim().length > 0; return typeof value === 'string' && value.trim().length > 0;
} }

View File

@ -13,17 +13,12 @@
"build": "tsc" "build": "tsc"
}, },
"dependencies": { "dependencies": {
"@aws-sdk/client-sqs": "^3.682.0",
"@azure/service-bus": "^7.9.5",
"@google-cloud/pubsub": "^4.8.0",
"amqplib": "^0.10.4",
"config": "^3.3.12", "config": "^3.3.12",
"express": "^4.21.1", "express": "^4.21.1",
"js-yaml": "^4.1.0", "js-yaml": "^4.1.0",
"kafkajs": "^2.2.4", "kafkajs": "^2.2.4",
"long": "^5.2.3", "long": "^5.2.3",
"uuid-parse": "^1.1.0", "uuid-parse": "^1.1.0",
"uuid-random": "^1.3.2",
"winston": "^3.16.0", "winston": "^3.16.0",
"winston-daily-rotate-file": "^5.0.0" "winston-daily-rotate-file": "^5.0.0"
}, },
@ -36,7 +31,6 @@
] ]
}, },
"devDependencies": { "devDependencies": {
"@types/amqplib": "^0.10.5",
"@types/config": "^3.3.5", "@types/config": "^3.3.5",
"@types/express": "~4.17.21", "@types/express": "~4.17.21",
"@types/node": "~20.17.6", "@types/node": "~20.17.6",

View File

@ -35,7 +35,6 @@ import { KeyObject } from 'tls';
import process, { exit, kill } from 'process'; import process, { exit, kill } from 'process';
// TODO: remove dependencies for other queue types
export class KafkaTemplate implements IQueue { export class KafkaTemplate implements IQueue {
private logger = _logger(`kafkaTemplate`); private logger = _logger(`kafkaTemplate`);

File diff suppressed because it is too large Load Diff