diff --git a/README.md b/README.md
index 6267cc3a7a..7776b1a964 100644
--- a/README.md
+++ b/README.md
@@ -123,9 +123,9 @@ ThingsBoard is a scalable, user-friendly, and device-agnostic IoT platform that
[](https://thingsboard.io/use-cases/scada/)
-[**Fleet tracking**](https://thingsboard.io/use-cases/fleet-tracking/)
+[**Site fleet tracking**](https://thingsboard.io/use-cases/site-fleet-tracking/)
-[](https://thingsboard.io/use-cases/fleet-tracking/)
+[](https://thingsboard.io/use-cases/site-fleet-tracking/)
[**Smart farming**](https://thingsboard.io/use-cases/smart-farming/)
diff --git a/TEST_FAST.md b/TEST_FAST.md
new file mode 100644
index 0000000000..c0f1174584
--- /dev/null
+++ b/TEST_FAST.md
@@ -0,0 +1,31 @@
+
+## Running tests in parallel with a reasonable memory usage
+
+```bash
+export MAVEN_OPTS="-Xmx1024m"
+export NODE_OPTIONS="--max_old_space_size=4096"
+export SUREFIRE_JAVA_OPTS="-Xmx1200m -Xss256k -XX:+ExitOnOutOfMemoryError"
+
+mvn clean install -T6 -DskipTests
+mvn test -pl='!application,!dao,!ui-ngx,!msa/js-executor,!msa/web-ui' -T4
+mvn test -pl dao -Dparallel=packages -DforkCount=4
+
+mvn test -pl application -Dsurefire.excludes='**/nosql/*Test.java' -Dtest='org.thingsboard.server.controller.**' -DforkCount=6 -Dparallel=classes -Dsurefire.rerunFailingTestsCount=2 -Dsurefire.failOnFlakeCount=5
+mvn test -pl application -Dsurefire.excludes='**/nosql/*Test.java' -Dtest='org.thingsboard.server.edge.**' -DforkCount=4 -Dparallel=packages -Dsurefire.rerunFailingTestsCount=2 -Dsurefire.failOnFlakeCount=5
+mvn test -pl application -Dsurefire.excludes='**/nosql/*Test.java' -Dtest='org.thingsboard.server.service.**' -DforkCount=6 -Dparallel=packages -Dsurefire.rerunFailingTestsCount=2 -Dsurefire.failOnFlakeCount=5
+mvn test -pl application -Dsurefire.excludes='**/nosql/*Test.java' -Dtest='org.thingsboard.server.transport.mqtt.**' -DforkCount=6 -Dparallel=classes -Dsurefire.rerunFailingTestsCount=2 -Dsurefire.failOnFlakeCount=5
+mvn test -pl application -Dsurefire.excludes='**/nosql/*Test.java' -Dtest='org.thingsboard.server.transport.coap.**' -DforkCount=6 -Dparallel=classes -Dsurefire.rerunFailingTestsCount=2 -Dsurefire.failOnFlakeCount=5
+mvn test -pl application -Dsurefire.excludes='**/nosql/*Test.java' -Dtest='org.thingsboard.server.transport.lwm2m.**' -DforkCount=6 -Dparallel=packages -Dsurefire.rerunFailingTestsCount=2 -Dsurefire.failOnFlakeCount=5
+mvn test -pl application -Dsurefire.excludes='**/nosql/*Test.java' -Dtest='**/*TestSuite.java' -DforkCount=4 -Dparallel=classes -Dsurefire.rerunFailingTestsCount=2 -Dsurefire.failOnFlakeCount=5
+
+#the rest of application tests
+mvn test -pl application -Dtest='
+!**/nosql/*Test.java,
+!org.thingsboard.server.controller.**,
+!org.thingsboard.server.edge.**,
+!org.thingsboard.server.service.**,
+!org.thingsboard.server.transport.mqtt.**,
+!org.thingsboard.server.transport.coap.**,
+!org.thingsboard.server.transport.lwm2m.**
+' -DforkCount=6 -Dparallel=packages -Dsurefire.rerunFailingTestsCount=2 -Dsurefire.failOnFlakeCount=5
+```
diff --git a/application/pom.xml b/application/pom.xml
index ab26655052..0f3c9623f9 100644
--- a/application/pom.xml
+++ b/application/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 4.3.0-RC
+ 4.4.0-SNAPSHOT
thingsboard
application
diff --git a/application/src/main/data/upgrade/basic/schema_update.sql b/application/src/main/data/upgrade/basic/schema_update.sql
index 66243461e2..a2dfeac358 100644
--- a/application/src/main/data/upgrade/basic/schema_update.sql
+++ b/application/src/main/data/upgrade/basic/schema_update.sql
@@ -14,69 +14,3 @@
-- limitations under the License.
--
--- UPDATE TENANT PROFILE CONFIGURATION START
-
-UPDATE tenant_profile
-SET profile_data = jsonb_set(
- profile_data,
- '{configuration}',
- jsonb_build_object(
- 'minAllowedScheduledUpdateIntervalInSecForCF', 10,
- 'maxRelationLevelPerCfArgument', 2,
- 'maxRelatedEntitiesToReturnPerCfArgument', 100,
- 'minAllowedDeduplicationIntervalInSecForCF', 10,
- 'minAllowedAggregationIntervalInSecForCF', 60,
- 'intermediateAggregationIntervalInSecForCF', 300,
- 'cfReevaluationCheckInterval', 60,
- 'alarmsReevaluationInterval', 60
- )
- ||
- jsonb_strip_nulls(profile_data -> 'configuration')
-)
-WHERE NOT (
- jsonb_strip_nulls(profile_data -> 'configuration') ?& ARRAY[
- 'minAllowedScheduledUpdateIntervalInSecForCF',
- 'maxRelationLevelPerCfArgument',
- 'maxRelatedEntitiesToReturnPerCfArgument',
- 'minAllowedDeduplicationIntervalInSecForCF',
- 'minAllowedAggregationIntervalInSecForCF',
- 'intermediateAggregationIntervalInSecForCF',
- 'cfReevaluationCheckInterval',
- 'alarmsReevaluationInterval'
- ]
-);
-
--- UPDATE TENANT PROFILE CONFIGURATION END
-
--- CALCULATED FIELD UNIQUE CONSTRAINT UPDATE START
-
-ALTER TABLE calculated_field DROP CONSTRAINT IF EXISTS calculated_field_unq_key;
-ALTER TABLE calculated_field ADD CONSTRAINT calculated_field_unq_key UNIQUE (entity_id, type, name);
-
--- CALCULATED FIELD UNIQUE CONSTRAINT UPDATE END
-
--- CALCULATED FIELD OUTPUT STRATEGY UPDATE START
-
-UPDATE calculated_field
-SET configuration = jsonb_set(
- configuration::jsonb,
- '{output}',
- (configuration::jsonb -> 'output')
- || jsonb_build_object(
- 'strategy',
- jsonb_build_object(
- 'type', 'RULE_CHAIN'
- )
- ),
- false
- )
-WHERE (configuration::jsonb -> 'output' -> 'strategy') IS NULL;
-
--- CALCULATED FIELD OUTPUT STRATEGY UPDATE END
-
--- REMOVAL OF CALCULATED FIELD LINKS PERSISTENCE START
-
-DROP TABLE IF EXISTS calculated_field_link;
-ANALYZE calculated_field;
-
--- REMOVAL OF CALCULATED FIELD LINKS PERSISTENCE END
diff --git a/application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldManagerMessageProcessor.java b/application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldManagerMessageProcessor.java
index 4de50b1ccf..a1d7477a36 100644
--- a/application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldManagerMessageProcessor.java
+++ b/application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldManagerMessageProcessor.java
@@ -425,7 +425,7 @@ public class CalculatedFieldManagerMessageProcessor extends AbstractContextAware
try {
cfCtx.init();
} catch (Exception e) {
- throw CalculatedFieldException.builder().ctx(cfCtx).eventEntity(cf.getEntityId()).cause(e).errorMessage("Failed to initialize CF context").build();
+ throw CalculatedFieldException.builder().ctx(cfCtx).eventEntity(cf.getEntityId()).cause(e).errorMessage(e.getMessage()).build();
}
calculatedFields.put(cf.getId(), cfCtx);
// We use copy on write lists to safely pass the reference to another actor for the iteration.
@@ -456,7 +456,7 @@ public class CalculatedFieldManagerMessageProcessor extends AbstractContextAware
try {
newCfCtx.init();
} catch (Exception e) {
- throw CalculatedFieldException.builder().ctx(newCfCtx).eventEntity(newCfCtx.getEntityId()).cause(e).errorMessage("Failed to initialize CF context").build();
+ throw CalculatedFieldException.builder().ctx(newCfCtx).eventEntity(newCfCtx.getEntityId()).cause(e).errorMessage(e.getMessage()).build();
} finally {
calculatedFields.put(newCf.getId(), newCfCtx);
List oldCfList = entityIdCalculatedFields.get(newCf.getEntityId());
@@ -819,7 +819,7 @@ public class CalculatedFieldManagerMessageProcessor extends AbstractContextAware
try {
cfCtx.init();
} catch (Exception e) {
- throw CalculatedFieldException.builder().ctx(cfCtx).eventEntity(cf.getEntityId()).cause(e).errorMessage("Failed to initialize CF context").build();
+ throw CalculatedFieldException.builder().ctx(cfCtx).eventEntity(cf.getEntityId()).cause(e).errorMessage(e.getMessage()).build();
} finally {
calculatedFields.put(cf.getId(), cfCtx);
// We use copy on write lists to safely pass the reference to another actor for the iteration.
diff --git a/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java b/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java
index 8ea989fc01..66695c396f 100644
--- a/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java
+++ b/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java
@@ -116,7 +116,6 @@ public class ThingsboardInstallService {
entityDatabaseSchemaService.createDatabaseIndexes();
// TODO: cleanup update code after each release
- systemDataLoaderService.updateDefaultNotificationConfigs(false);
// Runs upgrade scripts that are not possible in plain SQL.
dataUpdateService.updateData();
diff --git a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/CalculatedFieldCtx.java b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/CalculatedFieldCtx.java
index ab38287b89..5d7107b15e 100644
--- a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/CalculatedFieldCtx.java
+++ b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/CalculatedFieldCtx.java
@@ -387,7 +387,7 @@ public class CalculatedFieldCtx implements Closeable {
tbelExpressions.put(expression, engine);
} catch (Exception e) {
initialized = false;
- throw new RuntimeException("Failed to init calculated field ctx. Invalid expression syntax.", e);
+ throw new RuntimeException("Failed to initialize CF context. The script expression is invalid. Please check for syntax errors or unsupported functions.", e);
}
}
@@ -404,7 +404,7 @@ public class CalculatedFieldCtx implements Closeable {
simpleExpressions.put(expression, compiledExpression);
} else {
initialized = false;
- throw new RuntimeException("Failed to init calculated field ctx. Invalid expression syntax.");
+ throw new RuntimeException("Failed to initialize CF context. The expression has invalid syntax or unknown variables. Ensure all mathematical operators are correct.");
}
}
@@ -726,6 +726,9 @@ public class CalculatedFieldCtx implements Closeable {
return true;
}
}
+ if (cfType == CalculatedFieldType.PROPAGATION && !propagationArgument.equals(other.propagationArgument)) {
+ return true;
+ }
if (hasGeofencingZoneGroupConfigurationChanges(other)) {
return true;
}
diff --git a/application/src/main/java/org/thingsboard/server/service/install/DefaultDatabaseSchemaSettingsService.java b/application/src/main/java/org/thingsboard/server/service/install/DefaultDatabaseSchemaSettingsService.java
index d8bcf666f7..b692194382 100644
--- a/application/src/main/java/org/thingsboard/server/service/install/DefaultDatabaseSchemaSettingsService.java
+++ b/application/src/main/java/org/thingsboard/server/service/install/DefaultDatabaseSchemaSettingsService.java
@@ -31,7 +31,7 @@ public class DefaultDatabaseSchemaSettingsService implements DatabaseSchemaSetti
// This list should include all versions that are compatible for the upgrade in 4 digits format (like 4.2.0.0, etc.).
// The compatibility cycle usually breaks when we have some scripts written in Java that may not work after a new release.
// TODO: don't check the "patch" number, since upgrade is not required for patch releases
- private static final List SUPPORTED_VERSIONS_FOR_UPGRADE = List.of("4.2.1.0", "4.2.1.1", "4.2.1.2");
+ private static final List SUPPORTED_VERSIONS_FOR_UPGRADE = List.of("4.3.0.0", "4.3.0.1");
private final ProjectInfo projectInfo;
private final JdbcTemplate jdbcTemplate;
diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml
index 072e18af69..ef0d83d564 100644
--- a/application/src/main/resources/thingsboard.yml
+++ b/application/src/main/resources/thingsboard.yml
@@ -217,7 +217,7 @@ ui:
# Help parameters
help:
# Base URL for UI help assets
- base-url: "${UI_HELP_BASE_URL:https://raw.githubusercontent.com/thingsboard/thingsboard-ui-help/release-4.3}"
+ base-url: "${UI_HELP_BASE_URL:https://raw.githubusercontent.com/thingsboard/thingsboard-ui-help/release-4.4}"
# Database telemetry parameters
database:
diff --git a/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java b/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java
index 457f723644..6fbe9d9864 100644
--- a/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java
+++ b/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java
@@ -60,6 +60,7 @@ import org.springframework.test.web.servlet.ResultActions;
import org.springframework.test.web.servlet.ResultMatcher;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
+import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.context.WebApplicationContext;
@@ -349,7 +350,14 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest {
if (this.mockMvc == null) {
this.mockMvc = webAppContextSetup(webApplicationContext)
- .apply(springSecurity()).build();
+ .apply(springSecurity())
+ // conditional printing of non 2xx responses
+ .alwaysDo(result -> {
+ if (result.getResponse().getStatus() >= 400) {
+ MockMvcResultHandlers.log().handle(result);
+ }
+ })
+ .build();
}
loginSysAdmin();
@@ -423,7 +431,7 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest {
jdbcTemplate.execute("TRUNCATE TABLE notification");
- log.info("Executed web test teardown");
+ log.debug("Executed web test teardown");
}
private void verifyNoTenantsLeft() throws Exception {
diff --git a/application/src/test/java/org/thingsboard/server/controller/EdgeControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/EdgeControllerTest.java
index 43f08f42f9..9d50e3a62c 100644
--- a/application/src/test/java/org/thingsboard/server/controller/EdgeControllerTest.java
+++ b/application/src/test/java/org/thingsboard/server/controller/EdgeControllerTest.java
@@ -36,7 +36,10 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.DynamicPropertyRegistry;
+import org.springframework.test.context.DynamicPropertySource;
import org.springframework.test.context.TestPropertySource;
+import org.springframework.test.util.TestSocketUtils;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.common.util.ThingsBoardExecutors;
import org.thingsboard.server.common.data.AdminSettings;
@@ -99,6 +102,7 @@ import org.thingsboard.server.service.edge.instructions.EdgeUpgradeInstructionsS
import java.util.ArrayList;
import java.util.Collections;
+import java.util.Deque;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -121,7 +125,12 @@ import static org.thingsboard.server.edge.AbstractEdgeTest.CONNECT_MESSAGE_COUNT
public class EdgeControllerTest extends AbstractControllerTest {
public static final String EDGE_HOST = "localhost";
- public static final int EDGE_PORT = 7070;
+ public static final int EDGE_PORT = TestSocketUtils.findAvailableTcpPort();
+ @DynamicPropertySource
+ static void props(DynamicPropertyRegistry registry) {
+ log.debug("edges.rpc.port = {}", EDGE_PORT);
+ registry.add("edges.rpc.port", () -> EDGE_PORT);
+ }
private IdComparator idComparator = new IdComparator<>();
@@ -916,11 +925,11 @@ public class EdgeControllerTest extends AbstractControllerTest {
verifyFetchersMsgs(edgeImitator, savedDevice);
// verify queue msgs
- Assert.assertTrue(popDeviceProfileMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default"));
- Assert.assertTrue(popDeviceMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Test Sync Edge Device 1"));
- Assert.assertTrue(popDeviceCredentialsMsg(edgeImitator.getDownlinkMsgs(), savedDevice.getId()));
- Assert.assertTrue(popAssetProfileMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default"));
- Assert.assertTrue(popAssetMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Test Sync Edge Asset 1"));
+ Assert.assertTrue(popDeviceProfileMsg(edgeImitator.getDownlinkMsgsDeque(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default"));
+ Assert.assertTrue(popDeviceMsg(edgeImitator.getDownlinkMsgsDeque(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Test Sync Edge Device 1"));
+ Assert.assertTrue(popDeviceCredentialsMsg(edgeImitator.getDownlinkMsgsDeque(), savedDevice.getId()));
+ Assert.assertTrue(popAssetProfileMsg(edgeImitator.getDownlinkMsgsDeque(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default"));
+ Assert.assertTrue(popAssetMsg(edgeImitator.getDownlinkMsgsDeque(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Test Sync Edge Asset 1"));
printQueueMsgsIfNotEmpty(edgeImitator);
// 17 connect messages
@@ -1000,32 +1009,32 @@ public class EdgeControllerTest extends AbstractControllerTest {
}
private void verifyFetchersMsgs(EdgeImitator edgeImitator, Device savedDevice) {
- Assert.assertTrue(popQueueMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Main"));
- Assert.assertTrue(popRuleChainMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Edge Root Rule Chain"));
- Assert.assertTrue(popRuleChainMetadataMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, getEdgeRootRuleChainId(edgeImitator)));
- Assert.assertTrue(popAdminSettingsMsg(edgeImitator.getDownlinkMsgs(), "general"));
- Assert.assertTrue(popAdminSettingsMsg(edgeImitator.getDownlinkMsgs(), "mail"));
- Assert.assertTrue(popAdminSettingsMsg(edgeImitator.getDownlinkMsgs(), "connectivity"));
- Assert.assertTrue(popAdminSettingsMsg(edgeImitator.getDownlinkMsgs(), "jwt"));
- Assert.assertTrue(popDeviceProfileMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default"));
- Assert.assertTrue(popDeviceProfileMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default"));
- Assert.assertTrue(popAssetProfileMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default"));
- Assert.assertTrue(popDeviceProfileMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default"));
- Assert.assertTrue(popAssetProfileMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default"));
- Assert.assertTrue(popUserCredentialsMsg(edgeImitator.getDownlinkMsgs(), currentUserId));
- Assert.assertTrue(popUserMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, TENANT_ADMIN_EMAIL, Authority.TENANT_ADMIN));
- Assert.assertTrue(popCustomerMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Public"));
- Assert.assertTrue(popDeviceProfileMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default"));
- Assert.assertTrue(popDeviceMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Test Sync Edge Device 1"));
- Assert.assertTrue(popDeviceCredentialsMsg(edgeImitator.getDownlinkMsgs(), savedDevice.getId()));
- Assert.assertTrue(popAssetProfileMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default"));
- Assert.assertTrue(popAssetMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Test Sync Edge Asset 1"));
- Assert.assertTrue(popTenantMsg(edgeImitator.getDownlinkMsgs(), tenantId));
- Assert.assertTrue(popTenantProfileMsg(edgeImitator.getDownlinkMsgs(), tenantProfileId));
- Assert.assertTrue(popSyncCompletedMsg(edgeImitator.getDownlinkMsgs()));
+ Assert.assertTrue(popQueueMsg(edgeImitator.getDownlinkMsgsDeque(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Main"));
+ Assert.assertTrue(popRuleChainMsg(edgeImitator.getDownlinkMsgsDeque(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Edge Root Rule Chain"));
+ Assert.assertTrue(popRuleChainMetadataMsg(edgeImitator.getDownlinkMsgsDeque(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, getEdgeRootRuleChainId(edgeImitator)));
+ Assert.assertTrue(popAdminSettingsMsg(edgeImitator.getDownlinkMsgsDeque(), "general"));
+ Assert.assertTrue(popAdminSettingsMsg(edgeImitator.getDownlinkMsgsDeque(), "mail"));
+ Assert.assertTrue(popAdminSettingsMsg(edgeImitator.getDownlinkMsgsDeque(), "connectivity"));
+ Assert.assertTrue(popAdminSettingsMsg(edgeImitator.getDownlinkMsgsDeque(), "jwt"));
+ Assert.assertTrue(popDeviceProfileMsg(edgeImitator.getDownlinkMsgsDeque(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default"));
+ Assert.assertTrue(popDeviceProfileMsg(edgeImitator.getDownlinkMsgsDeque(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default"));
+ Assert.assertTrue(popAssetProfileMsg(edgeImitator.getDownlinkMsgsDeque(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default"));
+ Assert.assertTrue(popDeviceProfileMsg(edgeImitator.getDownlinkMsgsDeque(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default"));
+ Assert.assertTrue(popAssetProfileMsg(edgeImitator.getDownlinkMsgsDeque(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default"));
+ Assert.assertTrue(popUserCredentialsMsg(edgeImitator.getDownlinkMsgsDeque(), currentUserId));
+ Assert.assertTrue(popUserMsg(edgeImitator.getDownlinkMsgsDeque(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, TENANT_ADMIN_EMAIL, Authority.TENANT_ADMIN));
+ Assert.assertTrue(popCustomerMsg(edgeImitator.getDownlinkMsgsDeque(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Public"));
+ Assert.assertTrue(popDeviceProfileMsg(edgeImitator.getDownlinkMsgsDeque(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default"));
+ Assert.assertTrue(popDeviceMsg(edgeImitator.getDownlinkMsgsDeque(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Test Sync Edge Device 1"));
+ Assert.assertTrue(popDeviceCredentialsMsg(edgeImitator.getDownlinkMsgsDeque(), savedDevice.getId()));
+ Assert.assertTrue(popAssetProfileMsg(edgeImitator.getDownlinkMsgsDeque(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default"));
+ Assert.assertTrue(popAssetMsg(edgeImitator.getDownlinkMsgsDeque(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Test Sync Edge Asset 1"));
+ Assert.assertTrue(popTenantMsg(edgeImitator.getDownlinkMsgsDeque(), tenantId));
+ Assert.assertTrue(popTenantProfileMsg(edgeImitator.getDownlinkMsgsDeque(), tenantProfileId));
+ Assert.assertTrue(popSyncCompletedMsg(edgeImitator.getDownlinkMsgsDeque()));
}
- private boolean popQueueMsg(List messages, UpdateMsgType msgType, String name) {
+ private boolean popQueueMsg(Deque messages, UpdateMsgType msgType, String name) {
for (AbstractMessage message : messages) {
if (message instanceof QueueUpdateMsg queueUpdateMsg) {
Queue queue = JacksonUtil.fromString(queueUpdateMsg.getEntity(), Queue.class, true);
@@ -1039,7 +1048,7 @@ public class EdgeControllerTest extends AbstractControllerTest {
return false;
}
- private boolean popRuleChainMsg(List messages, UpdateMsgType msgType, String name) {
+ private boolean popRuleChainMsg(Deque messages, UpdateMsgType msgType, String name) {
for (AbstractMessage message : messages) {
if (message instanceof RuleChainUpdateMsg ruleChainUpdateMsg) {
RuleChain ruleChain = JacksonUtil.fromString(ruleChainUpdateMsg.getEntity(), RuleChain.class, true);
@@ -1055,7 +1064,7 @@ public class EdgeControllerTest extends AbstractControllerTest {
return false;
}
- private boolean popRuleChainMetadataMsg(List messages, UpdateMsgType msgType, RuleChainId ruleChainId) {
+ private boolean popRuleChainMetadataMsg(Deque messages, UpdateMsgType msgType, RuleChainId ruleChainId) {
for (AbstractMessage message : messages) {
if (message instanceof RuleChainMetadataUpdateMsg ruleChainMetadataUpdateMsg) {
RuleChainMetaData ruleChainMetaData = JacksonUtil.fromString(ruleChainMetadataUpdateMsg.getEntity(), RuleChainMetaData.class, true);
@@ -1070,7 +1079,7 @@ public class EdgeControllerTest extends AbstractControllerTest {
return false;
}
- private boolean popAdminSettingsMsg(List messages, String key) {
+ private boolean popAdminSettingsMsg(Deque messages, String key) {
for (AbstractMessage message : messages) {
if (message instanceof AdminSettingsUpdateMsg adminSettingsUpdateMsg) {
AdminSettings adminSettings = JacksonUtil.fromString(adminSettingsUpdateMsg.getEntity(), AdminSettings.class, true);
@@ -1084,7 +1093,7 @@ public class EdgeControllerTest extends AbstractControllerTest {
return false;
}
- private boolean popDeviceProfileMsg(List messages, UpdateMsgType msgType, String name) {
+ private boolean popDeviceProfileMsg(Deque messages, UpdateMsgType msgType, String name) {
for (AbstractMessage message : messages) {
if (message instanceof DeviceProfileUpdateMsg deviceProfileUpdateMsg) {
DeviceProfile deviceProfile = JacksonUtil.fromString(deviceProfileUpdateMsg.getEntity(), DeviceProfile.class, true);
@@ -1099,7 +1108,7 @@ public class EdgeControllerTest extends AbstractControllerTest {
return false;
}
- private boolean popDeviceMsg(List messages, UpdateMsgType msgType, String name) {
+ private boolean popDeviceMsg(Deque messages, UpdateMsgType msgType, String name) {
for (AbstractMessage message : messages) {
if (message instanceof DeviceUpdateMsg deviceUpdateMsg) {
Device device = JacksonUtil.fromString(deviceUpdateMsg.getEntity(), Device.class, true);
@@ -1114,7 +1123,7 @@ public class EdgeControllerTest extends AbstractControllerTest {
return false;
}
- private boolean popDeviceCredentialsMsg(List messages, DeviceId deviceId) {
+ private boolean popDeviceCredentialsMsg(Deque messages, DeviceId deviceId) {
for (AbstractMessage message : messages) {
if (message instanceof DeviceCredentialsUpdateMsg deviceCredentialsUpdateMsg) {
DeviceCredentials deviceCredentials = JacksonUtil.fromString(deviceCredentialsUpdateMsg.getEntity(), DeviceCredentials.class, true);
@@ -1128,7 +1137,7 @@ public class EdgeControllerTest extends AbstractControllerTest {
return false;
}
- private boolean popAssetProfileMsg(List messages, UpdateMsgType msgType, String name) {
+ private boolean popAssetProfileMsg(Deque messages, UpdateMsgType msgType, String name) {
for (AbstractMessage message : messages) {
if (message instanceof AssetProfileUpdateMsg assetProfileUpdateMsg) {
AssetProfile assetProfile = JacksonUtil.fromString(assetProfileUpdateMsg.getEntity(), AssetProfile.class, true);
@@ -1143,7 +1152,7 @@ public class EdgeControllerTest extends AbstractControllerTest {
return false;
}
- private boolean popAssetMsg(List messages, UpdateMsgType msgType, String name) {
+ private boolean popAssetMsg(Deque messages, UpdateMsgType msgType, String name) {
for (AbstractMessage message : messages) {
if (message instanceof AssetUpdateMsg assetUpdateMsg) {
Asset asset = JacksonUtil.fromString(assetUpdateMsg.getEntity(), Asset.class, true);
@@ -1158,7 +1167,7 @@ public class EdgeControllerTest extends AbstractControllerTest {
return false;
}
- private boolean popUserCredentialsMsg(List messages, UserId userId) {
+ private boolean popUserCredentialsMsg(Deque messages, UserId userId) {
for (AbstractMessage message : messages) {
if (message instanceof UserCredentialsUpdateMsg userCredentialsUpdateMsg) {
UserCredentials userCredentials = JacksonUtil.fromString(userCredentialsUpdateMsg.getEntity(), UserCredentials.class, true);
@@ -1172,7 +1181,7 @@ public class EdgeControllerTest extends AbstractControllerTest {
return false;
}
- private boolean popUserMsg(List messages, UpdateMsgType msgType, String email, Authority authority) {
+ private boolean popUserMsg(Deque messages, UpdateMsgType msgType, String email, Authority authority) {
for (AbstractMessage message : messages) {
if (message instanceof UserUpdateMsg userUpdateMsg) {
User user = JacksonUtil.fromString(userUpdateMsg.getEntity(), User.class, true);
@@ -1188,7 +1197,7 @@ public class EdgeControllerTest extends AbstractControllerTest {
return false;
}
- private boolean popCustomerMsg(List messages, UpdateMsgType msgType, String title) {
+ private boolean popCustomerMsg(Deque messages, UpdateMsgType msgType, String title) {
for (AbstractMessage message : messages) {
if (message instanceof CustomerUpdateMsg customerUpdateMsg) {
Customer customer = JacksonUtil.fromString(customerUpdateMsg.getEntity(), Customer.class, true);
@@ -1203,7 +1212,7 @@ public class EdgeControllerTest extends AbstractControllerTest {
return false;
}
- private boolean popTenantMsg(List messages, TenantId tenantId1) {
+ private boolean popTenantMsg(Deque messages, TenantId tenantId1) {
for (AbstractMessage message : messages) {
if (message instanceof TenantUpdateMsg tenantUpdateMsg) {
Tenant tenant = JacksonUtil.fromString(tenantUpdateMsg.getEntity(), Tenant.class, true);
@@ -1218,7 +1227,7 @@ public class EdgeControllerTest extends AbstractControllerTest {
return false;
}
- private boolean popTenantProfileMsg(List messages, TenantProfileId tenantProfileId) {
+ private boolean popTenantProfileMsg(Deque messages, TenantProfileId tenantProfileId) {
for (AbstractMessage message : messages) {
if (message instanceof TenantProfileUpdateMsg tenantProfileUpdateMsg) {
TenantProfile tenantProfile = JacksonUtil.fromString(tenantProfileUpdateMsg.getEntity(), TenantProfile.class, true);
@@ -1233,7 +1242,7 @@ public class EdgeControllerTest extends AbstractControllerTest {
return false;
}
- private boolean popSyncCompletedMsg(List messages) {
+ private boolean popSyncCompletedMsg(Deque messages) {
for (AbstractMessage message : messages) {
if (message instanceof SyncCompletedMsg) {
messages.remove(message);
diff --git a/application/src/test/java/org/thingsboard/server/controller/EntityViewControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/EntityViewControllerTest.java
index 6d095dc95f..a4d6b62a0c 100644
--- a/application/src/test/java/org/thingsboard/server/controller/EntityViewControllerTest.java
+++ b/application/src/test/java/org/thingsboard/server/controller/EntityViewControllerTest.java
@@ -38,6 +38,8 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.DynamicPropertyRegistry;
+import org.springframework.test.context.DynamicPropertySource;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.web.servlet.ResultActions;
import org.thingsboard.common.util.ThingsBoardExecutors;
@@ -88,6 +90,8 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID;
+import static org.thingsboard.server.transport.mqtt.AbstractMqttIntegrationTest.MQTT_PORT;
+import static org.thingsboard.server.transport.mqtt.AbstractMqttIntegrationTest.MQTT_URL;
@TestPropertySource(properties = {
"transport.mqtt.enabled=true",
@@ -97,6 +101,12 @@ import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID;
@ContextConfiguration(classes = {EntityViewControllerTest.Config.class})
@DaoSqlTest
public class EntityViewControllerTest extends AbstractControllerTest {
+ @DynamicPropertySource
+ static void props(DynamicPropertyRegistry registry) {
+ log.warn("transport.mqtt.bind_port = {}", MQTT_PORT);
+ registry.add("transport.mqtt.bind_port", () -> MQTT_PORT);
+ }
+
static final TypeReference> PAGE_DATA_ENTITY_VIEW_TYPE_REF = new TypeReference<>() {
};
static final TypeReference> PAGE_DATA_ENTITY_VIEW_INFO_TYPE_REF = new TypeReference<>() {
@@ -683,7 +693,7 @@ public class EntityViewControllerTest extends AbstractControllerTest {
String viewDeviceId = testDevice.getId().getId().toString();
String clientId = MqttAsyncClient.generateClientId();
- MqttAsyncClient client = new MqttAsyncClient("tcp://localhost:1883", clientId, new MemoryPersistence());
+ MqttAsyncClient client = new MqttAsyncClient(MQTT_URL, clientId, new MemoryPersistence());
MqttConnectOptions options = new MqttConnectOptions();
options.setUserName(accessToken);
@@ -734,7 +744,7 @@ public class EntityViewControllerTest extends AbstractControllerTest {
assertNotNull(accessToken);
String clientId = MqttAsyncClient.generateClientId();
- MqttAsyncClient client = new MqttAsyncClient("tcp://localhost:1883", clientId, new MemoryPersistence());
+ MqttAsyncClient client = new MqttAsyncClient(MQTT_URL, clientId, new MemoryPersistence());
MqttConnectOptions options = new MqttConnectOptions();
options.setUserName(accessToken);
diff --git a/application/src/test/java/org/thingsboard/server/edge/AbstractEdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/AbstractEdgeTest.java
index 3629d7ff49..260476fe4d 100644
--- a/application/src/test/java/org/thingsboard/server/edge/AbstractEdgeTest.java
+++ b/application/src/test/java/org/thingsboard/server/edge/AbstractEdgeTest.java
@@ -27,7 +27,10 @@ import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.test.context.DynamicPropertyRegistry;
+import org.springframework.test.context.DynamicPropertySource;
import org.springframework.test.context.TestPropertySource;
+import org.springframework.test.util.TestSocketUtils;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.AdminSettings;
import org.thingsboard.server.common.data.Customer;
@@ -118,6 +121,15 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
})
@Slf4j
abstract public class AbstractEdgeTest extends AbstractControllerTest {
+
+ public static final String EDGE_HOST = "localhost";
+ public static final int EDGE_PORT = TestSocketUtils.findAvailableTcpPort();
+ @DynamicPropertySource
+ static void props(DynamicPropertyRegistry registry) {
+ log.debug("edges.rpc.port = {}", EDGE_PORT);
+ registry.add("edges.rpc.port", () -> EDGE_PORT);
+ }
+
public static final Integer CONNECT_MESSAGE_COUNT = 17;
public static final Integer INSTALLATION_MESSAGE_COUNT = 8;
public static final Integer SYNC_MESSAGE_COUNT = CONNECT_MESSAGE_COUNT + INSTALLATION_MESSAGE_COUNT;
@@ -144,7 +156,7 @@ abstract public class AbstractEdgeTest extends AbstractControllerTest {
//8 installation messages
installation();
- edgeImitator = new EdgeImitator("localhost", 7070, edge.getRoutingKey(), edge.getSecret());
+ edgeImitator = new EdgeImitator(EDGE_HOST, EDGE_PORT, edge.getRoutingKey(), edge.getSecret());
// 17 connect messages + 8 installation messages
edgeImitator.expectMessageAmount(SYNC_MESSAGE_COUNT);
edgeImitator.ignoreType(OAuth2ClientUpdateMsg.class);
diff --git a/application/src/test/java/org/thingsboard/server/edge/DeviceEdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/DeviceEdgeTest.java
index 9292d5bc06..9349f79c02 100644
--- a/application/src/test/java/org/thingsboard/server/edge/DeviceEdgeTest.java
+++ b/application/src/test/java/org/thingsboard/server/edge/DeviceEdgeTest.java
@@ -22,10 +22,13 @@ import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.gson.JsonObject;
import com.google.protobuf.AbstractMessage;
import io.netty.handler.codec.mqtt.MqttQoS;
+import lombok.extern.slf4j.Slf4j;
import org.awaitility.Awaitility;
import org.junit.Assert;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.test.context.DynamicPropertyRegistry;
+import org.springframework.test.context.DynamicPropertySource;
import org.springframework.test.context.TestPropertySource;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.adaptor.JsonConverter;
@@ -84,12 +87,19 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.thingsboard.server.gen.edge.v1.UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE;
+import static org.thingsboard.server.transport.mqtt.AbstractMqttIntegrationTest.MQTT_PORT;
@TestPropertySource(properties = {
"transport.mqtt.enabled=true"
})
+@Slf4j
@DaoSqlTest
public class DeviceEdgeTest extends AbstractEdgeTest {
+ @DynamicPropertySource
+ static void props(DynamicPropertyRegistry registry) {
+ log.warn("transport.mqtt.bind_port = {}", MQTT_PORT);
+ registry.add("transport.mqtt.bind_port", () -> MQTT_PORT);
+ }
private static final String DEFAULT_DEVICE_TYPE = "default";
diff --git a/application/src/test/java/org/thingsboard/server/edge/imitator/EdgeImitator.java b/application/src/test/java/org/thingsboard/server/edge/imitator/EdgeImitator.java
index 8b2cef2e8b..8cf2b309f6 100644
--- a/application/src/test/java/org/thingsboard/server/edge/imitator/EdgeImitator.java
+++ b/application/src/test/java/org/thingsboard/server/edge/imitator/EdgeImitator.java
@@ -69,10 +69,12 @@ import org.thingsboard.server.gen.edge.v1.WidgetsBundleUpdateMsg;
import java.lang.reflect.Field;
import java.util.ArrayList;
+import java.util.Deque;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
+import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
@@ -103,8 +105,17 @@ public class EdgeImitator {
@Getter
private EdgeConfiguration configuration;
- @Getter
- private final List downlinkMsgs;
+ private final ConcurrentLinkedDeque downlinkMsgs;
+
+ //Returns collection copy as Unmodifiable list
+ //This addressing the issue: DeviceEdgeTest>AbstractEdgeTest.setupEdgeTest:212->AbstractEdgeTest.verifyEdgeConnectionAndInitialData:306->AbstractEdgeTest.validateMsgsCnt:387 » ConcurrentModification
+ public List getDownlinkMsgs() {
+ return downlinkMsgs.stream().toList();
+ }
+
+ public Deque getDownlinkMsgsDeque() {
+ return downlinkMsgs;
+ }
@Getter
private UplinkResponseMsg latestResponseMsg;
@@ -113,7 +124,7 @@ public class EdgeImitator {
edgeRpcClient = new EdgeGrpcClient();
messagesLatch = new CountDownLatch(0);
responsesLatch = new CountDownLatch(0);
- downlinkMsgs = new ArrayList<>();
+ downlinkMsgs = new ConcurrentLinkedDeque<>();
ignoredTypes = new ArrayList<>();
this.routingKey = routingKey;
this.routingSecret = routingSecret;
@@ -458,7 +469,7 @@ public class EdgeImitator {
}
public AbstractMessage getLatestMessage() {
- return downlinkMsgs.get(downlinkMsgs.size() - 1);
+ return downlinkMsgs.peekLast();
}
public void ignoreType(Class extends AbstractMessage> type) {
diff --git a/application/src/test/java/org/thingsboard/server/service/notification/NotificationRuleApiTest.java b/application/src/test/java/org/thingsboard/server/service/notification/NotificationRuleApiTest.java
index 89f743aee4..e3342dbf43 100644
--- a/application/src/test/java/org/thingsboard/server/service/notification/NotificationRuleApiTest.java
+++ b/application/src/test/java/org/thingsboard/server/service/notification/NotificationRuleApiTest.java
@@ -238,12 +238,12 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest {
.set("createAlarm", BooleanNode.TRUE);
postAttributes(device.getId(), AttributeScope.SERVER_SCOPE, attr.toString());
- await().atMost(10, TimeUnit.SECONDS)
+ await().atMost(TIMEOUT, TimeUnit.SECONDS)
.until(() -> alarmSubscriptionService.findLatestByOriginatorAndType(tenantId, device.getId(), alarmType) != null);
Alarm alarm = alarmSubscriptionService.findLatestByOriginatorAndType(tenantId, device.getId(), alarmType);
long ts = System.currentTimeMillis();
- await().atMost(15, TimeUnit.SECONDS)
+ await().atMost(TIMEOUT, TimeUnit.SECONDS)
.until(() -> clients.values().stream().allMatch(client -> client.getLastDataUpdate() != null));
clients.forEach((expectedDelay, wsClient) -> {
Notification notification = wsClient.getLastDataUpdate().getUpdate();
@@ -291,8 +291,8 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest {
alarm.setOriginator(device.getId());
alarm = doPost("/api/alarm", alarm, Alarm.class);
- await().atMost(15, TimeUnit.SECONDS)
- .pollDelay(2, TimeUnit.SECONDS)
+ await().atMost(TIMEOUT, TimeUnit.SECONDS)
+ .pollDelay(1, TimeUnit.SECONDS)
.untilAsserted(() -> {
List notifications = getMyNotifications(false, 10);
assertThat(notifications).singleElement().matches(notification -> {
@@ -345,7 +345,7 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest {
.set("createAlarm", BooleanNode.TRUE);
postAttributes(device.getId(), AttributeScope.SERVER_SCOPE, attr.toString());
- await().atMost(10, TimeUnit.SECONDS)
+ await().atMost(TIMEOUT, TimeUnit.SECONDS)
.until(() -> alarmSubscriptionService.findLatestByOriginatorAndType(tenantId, device.getId(), alarmType) != null);
Alarm alarm = alarmSubscriptionService.findLatestByOriginatorAndType(tenantId, device.getId(), alarmType);
getWsClient().waitForUpdate(true);
@@ -355,14 +355,14 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest {
assertThat(notification.getInfo()).asInstanceOf(type(AlarmNotificationInfo.class))
.extracting(AlarmNotificationInfo::getAlarmId).isEqualTo(alarm.getUuidId());
- await().atMost(10, TimeUnit.SECONDS).until(() -> findNotificationRequests(EntityType.ALARM).getTotalElements() == escalationTable.size());
+ await().atMost(TIMEOUT, TimeUnit.SECONDS).until(() -> findNotificationRequests(EntityType.ALARM).getTotalElements() == escalationTable.size());
NotificationRequestInfo scheduledNotificationRequest = findNotificationRequests(EntityType.ALARM).getData().stream()
.filter(NotificationRequest::isScheduled)
.findFirst().orElse(null);
assertThat(scheduledNotificationRequest).extracting(NotificationRequest::getInfo).isEqualTo(notification.getInfo());
alarmSubscriptionService.clearAlarm(tenantId, alarm.getId(), System.currentTimeMillis(), null);
- await().atMost(5, TimeUnit.SECONDS).untilAsserted(() -> {
+ await().atMost(TIMEOUT, TimeUnit.SECONDS).untilAsserted(() -> {
assertThat(findNotificationRequests(EntityType.ALARM).getData()).filteredOn(NotificationRequest::isScheduled).isEmpty();
});
}
@@ -480,7 +480,7 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest {
}
loginTenantAdmin();
- List notifications = await().atMost(15, TimeUnit.SECONDS)
+ List notifications = await().atMost(TIMEOUT, TimeUnit.SECONDS)
.until(() -> getMyNotifications(true, 10).stream()
.filter(notification -> notification.getType() == NotificationType.RATE_LIMITS)
.collect(Collectors.toList()), list -> list.size() == 3);
@@ -500,7 +500,7 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest {
});
loginSysAdmin();
- notifications = await().atMost(15, TimeUnit.SECONDS)
+ notifications = await().atMost(TIMEOUT, TimeUnit.SECONDS)
.until(() -> getMyNotifications(true, 10).stream()
.filter(notification -> notification.getType() == NotificationType.RATE_LIMITS)
.collect(Collectors.toList()), list -> list.size() == 1);
@@ -721,7 +721,7 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest {
.updateInfo(new UpdateMessage(true, "CHANGED", "test",
"test", "test", "test"))
.build());
- await().atMost(5, TimeUnit.SECONDS)
+ await().atMost(TIMEOUT, TimeUnit.SECONDS)
.untilAsserted(() -> {
assertThat(getMyNotifications(false, 100)).size().isEqualTo(2);
});
@@ -809,7 +809,7 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest {
method.invoke(systemInfoService);
}
- await().atMost(10, TimeUnit.SECONDS).until(() -> getMyNotifications(false, 100).size() == 1);
+ await().atMost(TIMEOUT, TimeUnit.SECONDS).until(() -> getMyNotifications(false, 100).size() == 1);
Notification notification = getMyNotifications(false, 100).get(0);
assertThat(notification.getSubject()).isEqualTo("Warning: RAM shortage");
assertThat(notification.getText()).isEqualTo("RAM shortage");
@@ -836,7 +836,7 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest {
.build());
TimeUnit.MILLISECONDS.sleep(300);
}
- await().atMost(10, TimeUnit.SECONDS).until(() -> getMyNotifications(false, 100).size() == 1);
+ await().atMost(TIMEOUT, TimeUnit.SECONDS).until(() -> getMyNotifications(false, 100).size() == 1);
Notification notification = getMyNotifications(false, 100).get(0);
assertThat(notification.getSubject()).isEqualTo("Warning: RAM shortage");
assertThat(notification.getText()).isEqualTo("RAM shortage");
@@ -848,7 +848,7 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest {
.serviceType("serviceType")
.serviceId("serviceId")
.build());
- await("").atMost(5, TimeUnit.SECONDS).untilAsserted(() -> assertThat(getMyNotifications(false, 100)).size().isOne());
+ await("").atMost(TIMEOUT, TimeUnit.SECONDS).untilAsserted(() -> assertThat(getMyNotifications(false, 100)).size().isOne());
}
@Test
@@ -887,7 +887,7 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest {
method.invoke(systemInfoService);
- await().atMost(10, TimeUnit.SECONDS).until(() -> getMyNotifications(false, 100).size() == 1);
+ await().atMost(TIMEOUT, TimeUnit.SECONDS).until(() -> getMyNotifications(false, 100).size() == 1);
Notification notification = getMyNotifications(false, 100).get(0);
assertThat(notification.getSubject()).isEqualTo("Warning: RAM shortage");
assertThat(notification.getText()).isEqualTo("RAM shortage");
diff --git a/application/src/test/java/org/thingsboard/server/transport/AbstractTransportIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/AbstractTransportIntegrationTest.java
index 0badcd2656..c91fea282d 100644
--- a/application/src/test/java/org/thingsboard/server/transport/AbstractTransportIntegrationTest.java
+++ b/application/src/test/java/org/thingsboard/server/transport/AbstractTransportIntegrationTest.java
@@ -28,10 +28,7 @@ import java.util.concurrent.atomic.AtomicInteger;
@Slf4j
public abstract class AbstractTransportIntegrationTest extends AbstractControllerTest {
- protected static final int DEFAULT_WAIT_TIMEOUT_SECONDS = 30;
-
- protected static final String MQTT_URL = "tcp://localhost:1883";
- protected static final String COAP_BASE_URL = "coap://localhost:5683/api/v1/";
+ public static final int DEFAULT_WAIT_TIMEOUT_SECONDS = 30;
protected static final AtomicInteger atomicInteger = new AtomicInteger(2);
diff --git a/application/src/test/java/org/thingsboard/server/transport/coap/AbstractCoapIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/coap/AbstractCoapIntegrationTest.java
index 7240ab0afa..69d40f5409 100644
--- a/application/src/test/java/org/thingsboard/server/transport/coap/AbstractCoapIntegrationTest.java
+++ b/application/src/test/java/org/thingsboard/server/transport/coap/AbstractCoapIntegrationTest.java
@@ -16,7 +16,10 @@
package org.thingsboard.server.transport.coap;
import lombok.extern.slf4j.Slf4j;
+import org.springframework.test.context.DynamicPropertyRegistry;
+import org.springframework.test.context.DynamicPropertySource;
import org.springframework.test.context.TestPropertySource;
+import org.springframework.test.util.TestSocketUtils;
import org.thingsboard.server.common.data.CoapDeviceType;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceProfile;
@@ -40,6 +43,7 @@ import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadCo
import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration;
import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.transport.AbstractTransportIntegrationTest;
+import org.thingsboard.server.utils.PortFinder;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@@ -52,6 +56,16 @@ import static org.junit.Assert.assertNotNull;
@Slf4j
public abstract class AbstractCoapIntegrationTest extends AbstractTransportIntegrationTest {
+ public static final String COAP_HOST = "localhost";
+ public static final int COAP_PORT = PortFinder.findAvailableUdpPort();
+ public static final String COAP_BASE_URL = "coap://" + COAP_HOST + ":" + COAP_PORT + "/api/v1/";
+
+ @DynamicPropertySource
+ static void props(DynamicPropertyRegistry registry) {
+ log.warn("coap.bind_port = {}", COAP_PORT);
+ registry.add("coap.bind_port", () -> COAP_PORT);
+ }
+
protected final byte[] EMPTY_PAYLOAD = new byte[0];
protected CoapTestClient client;
protected static final String PAYLOAD_VALUES_STR = "{\"key1\":\"value1\", \"key2\":true, \"key3\": 3.0, \"key4\": 4," +
diff --git a/application/src/test/java/org/thingsboard/server/transport/coap/CoapTestClient.java b/application/src/test/java/org/thingsboard/server/transport/coap/CoapTestClient.java
index ce72d68fc6..df7e9babc9 100644
--- a/application/src/test/java/org/thingsboard/server/transport/coap/CoapTestClient.java
+++ b/application/src/test/java/org/thingsboard/server/transport/coap/CoapTestClient.java
@@ -28,9 +28,10 @@ import org.thingsboard.server.common.msg.session.FeatureType;
import java.io.IOException;
+import static org.thingsboard.server.transport.coap.AbstractCoapIntegrationTest.COAP_BASE_URL;
+
public class CoapTestClient {
- private static final String COAP_BASE_URL = "coap://localhost:5683/api/v1/";
private static final long CLIENT_REQUEST_TIMEOUT = 60000L;
private final CoapClient client;
diff --git a/application/src/test/java/org/thingsboard/server/transport/coap/security/AbstractCoapSecurityIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/coap/security/AbstractCoapSecurityIntegrationTest.java
index cfb449aa33..04434ff53d 100644
--- a/application/src/test/java/org/thingsboard/server/transport/coap/security/AbstractCoapSecurityIntegrationTest.java
+++ b/application/src/test/java/org/thingsboard/server/transport/coap/security/AbstractCoapSecurityIntegrationTest.java
@@ -21,6 +21,8 @@ import lombok.extern.slf4j.Slf4j;
import org.eclipse.californium.core.CoapResponse;
import org.eclipse.californium.core.coap.CoAP;
import org.junit.Assert;
+import org.springframework.test.context.DynamicPropertyRegistry;
+import org.springframework.test.context.DynamicPropertySource;
import org.springframework.test.context.TestPropertySource;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.CoapDeviceType;
@@ -37,10 +39,10 @@ import org.thingsboard.server.transport.coap.AbstractCoapIntegrationTest;
import org.thingsboard.server.transport.coap.x509.CertPrivateKey;
import org.thingsboard.server.transport.coap.x509.CoapClientX509Test;
import org.thingsboard.server.transport.coap.CoapTestConfigProperties;
+import org.thingsboard.server.utils.PortFinder;
import java.io.IOException;
import java.io.InputStream;
-import java.net.ServerSocket;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.PrivateKey;
@@ -51,9 +53,7 @@ import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
-import java.util.concurrent.TimeUnit;
-import static org.awaitility.Awaitility.await;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
@@ -70,7 +70,17 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
"transport.coap.enabled=true",
})
public abstract class AbstractCoapSecurityIntegrationTest extends AbstractCoapIntegrationTest {
- private static final String COAPS_BASE_URL = "coaps://localhost:5684/api/v1/";
+
+ public static final String COAPS_HOST = "localhost";
+ public static final int COAPS_PORT = PortFinder.findAvailableUdpPort();
+ public static final String COAPS_BASE_URL = "coaps://" + COAPS_HOST + ":" + COAPS_PORT + "/api/v1/";
+
+ @DynamicPropertySource
+ static void props(DynamicPropertyRegistry registry) {
+ log.warn("coap.dtls.bind_port = {}", COAPS_PORT);
+ registry.add("coap.dtls.bind_port", () -> COAPS_PORT);
+ }
+
protected final String CREDENTIALS_PATH = "coap/credentials/";
protected final String CREDENTIALS_PATH_CLIENT = CREDENTIALS_PATH + "client/";
protected final String CREDENTIALS_PATH_CLIENT_CERT_PEM = CREDENTIALS_PATH_CLIENT + "cert.pem";
@@ -160,13 +170,10 @@ public abstract class AbstractCoapSecurityIntegrationTest extends AbstractCoapIn
CertPrivateKey certPrivateKey = new CertPrivateKey(CREDENTIALS_PATH_CLIENT_CERT_PEM, CREDENTIALS_PATH_CLIENT_KEY_PEM);
CertPrivateKey certPrivateKey_01 = new CertPrivateKey(CREDENTIALS_PATH_CLIENT + "cert_01.pem",
CREDENTIALS_PATH_CLIENT + "key_01.pem");
- Integer fixedPort = getFreePort();
+ int fixedPort = PortFinder.findAvailableUdpPort();
CoapClientX509Test clientX509 = clientX509UpdateTest(FeatureType.ATTRIBUTES, certPrivateKey,
"CoapX509TrustNo_" + FeatureType.TELEMETRY.name(), deviceProfile.getId(), fixedPort);
clientX509.disconnect();
- await("Need to make port " + fixedPort + " free")
- .atMost(40, TimeUnit.SECONDS)
- .until(() -> isPortAvailable(fixedPort));
CoapClientX509Test clientX509_01 = clientX509UpdateTest(FeatureType.ATTRIBUTES, certPrivateKey_01,
"CoapX509TrustNo_" + FeatureType.TELEMETRY.name() + "_01", deviceProfile.getId(),
fixedPort, PAYLOAD_VALUES_STR_01);
@@ -274,19 +281,5 @@ public abstract class AbstractCoapSecurityIntegrationTest extends AbstractCoapIn
}
}
- private static int getFreePort() throws IOException {
- try (ServerSocket socket = new ServerSocket(0)) {
- return socket.getLocalPort();
- }
- }
-
- private static boolean isPortAvailable(int port) {
- try (ServerSocket serverSocket = new ServerSocket(port)) {
- serverSocket.setReuseAddress(true);
- return true;
- } catch (IOException e) {
- return false;
- }
- }
}
diff --git a/application/src/test/java/org/thingsboard/server/transport/coap/x509/CoapClientX509Test.java b/application/src/test/java/org/thingsboard/server/transport/coap/x509/CoapClientX509Test.java
index 0f65298d86..ebbed25243 100644
--- a/application/src/test/java/org/thingsboard/server/transport/coap/x509/CoapClientX509Test.java
+++ b/application/src/test/java/org/thingsboard/server/transport/coap/x509/CoapClientX509Test.java
@@ -51,6 +51,7 @@ import java.util.List;
import java.util.concurrent.TimeUnit;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static org.awaitility.Awaitility.await;
import static org.eclipse.californium.core.config.CoapConfig.DEFAULT_BLOCKWISE_STATUS_LIFETIME_IN_SECONDS;
import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_AUTO_HANDSHAKE_TIMEOUT;
import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_CIPHER_SUITES;
@@ -71,6 +72,8 @@ import static org.eclipse.californium.scandium.config.DtlsConfig.MODULE;
import static org.eclipse.californium.scandium.dtls.SignatureAndHashAlgorithm.SHA256_WITH_ECDSA;
import static org.eclipse.californium.scandium.dtls.SignatureAndHashAlgorithm.SHA256_WITH_RSA;
import static org.eclipse.californium.scandium.dtls.SignatureAndHashAlgorithm.SHA384_WITH_ECDSA;
+import static org.thingsboard.server.transport.AbstractTransportIntegrationTest.DEFAULT_WAIT_TIMEOUT_SECONDS;
+import static org.thingsboard.server.utils.PortFinder.isUDPPortAvailable;
@Slf4j
public class CoapClientX509Test {
@@ -82,6 +85,7 @@ public class CoapClientX509Test {
private final Configuration config;
private final CertPrivateKey certPrivateKey;
private final String coapsBaseUrl;
+ private final Integer fixedPort;
@Getter
private CoAP.Type type = CoAP.Type.CON;
@@ -90,6 +94,7 @@ public class CoapClientX509Test {
this.certPrivateKey = certPrivateKey;
this.coapsBaseUrl = coapsBaseUrl;
this.config = createConfiguration();
+ this.fixedPort = fixedPort;
this.dtlsConnector = createDTLSConnector(fixedPort);
this.clientX509 = createClient(getFeatureTokenUrl(featureType));
}
@@ -97,6 +102,15 @@ public class CoapClientX509Test {
if (clientX509 != null) {
clientX509.shutdown();
}
+ if (dtlsConnector != null) {
+ dtlsConnector.destroy();
+ if (fixedPort != null) {
+ log.debug("Awaiting releasing UDP fixedPort {}", fixedPort);
+ await("Await client UDP port " + fixedPort + " to disconnect")
+ .atMost(DEFAULT_WAIT_TIMEOUT_SECONDS, TimeUnit.SECONDS)
+ .until(() -> isUDPPortAvailable(fixedPort));
+ }
+ }
}
public CoapResponse postMethod(String requestBody) throws ConnectorException, IOException {
diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java
index 096903f8df..f3a3212e67 100644
--- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java
+++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java
@@ -19,6 +19,7 @@ import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
+import jnr.ffi.annotations.In;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.awaitility.core.ConditionTimeoutException;
@@ -38,6 +39,8 @@ import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.mock.mockito.SpyBean;
import org.springframework.http.HttpStatus;
+import org.springframework.test.context.DynamicPropertyRegistry;
+import org.springframework.test.context.DynamicPropertySource;
import org.springframework.test.context.TestPropertySource;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.common.util.ThingsBoardExecutors;
@@ -84,9 +87,8 @@ import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext;
import org.thingsboard.server.transport.lwm2m.server.client.ResourceUpdateResult;
import org.thingsboard.server.transport.lwm2m.server.uplink.DefaultLwM2mUplinkMsgHandler;
import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler;
+import org.thingsboard.server.utils.PortFinder;
-import java.io.IOException;
-import java.net.ServerSocket;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -141,21 +143,35 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractTransportInte
private LwM2mClientContext clientContextTest;
// Lwm2m Server
- public static final int port = 5685;
- public static final int securityPort = 5686;
- public static final int portBs = 5687;
- public static final int securityPortBs = 5688;
+ public static final String LWM2M_HOST = "localhost";
+ public static final int LWM2M_PORT = PortFinder.findAvailableUdpPort(); // 5685
+ public static final int LWM2MS_PORT = PortFinder.findAvailableUdpPort(); //5686
+
+ public static final String LWM2M_BOOTSTRAP_HOST = "localhost";
+ public static final int LWM2M_BOOTSTRAP_PORT = PortFinder.findAvailableUdpPort(); // 5687
+ public static final int LWM2MS_BOOTSTRAP_PORT = PortFinder.findAvailableUdpPort(); // 5688
+
+ @DynamicPropertySource
+ static void props(DynamicPropertyRegistry registry) {
+ log.warn("transport.lwm2m.server.bind_port = {}", LWM2M_PORT);
+ registry.add("transport.lwm2m.server.bind_port", () -> LWM2M_PORT);
+ log.warn("transport.lwm2m.server.security.bind_port = {}", LWM2MS_PORT);
+ registry.add("transport.lwm2m.server.security.bind_port", () -> LWM2MS_PORT);
+
+ log.warn("transport.lwm2m.bootstrap.bind_port = {}", LWM2M_BOOTSTRAP_PORT);
+ registry.add("transport.lwm2m.bootstrap.bind_port", () -> LWM2M_BOOTSTRAP_PORT);
+ log.warn("transport.lwm2m.bootstrap.security.bind_port = {}", LWM2MS_BOOTSTRAP_PORT);
+ registry.add("transport.lwm2m.bootstrap.security.bind_port", () -> LWM2MS_BOOTSTRAP_PORT);
+ }
- public static final String host = "localhost";
- public static final String hostBs = "localhost";
public static final Integer shortServerId = 123;
public static final String COAP = "coap://";
public static final String COAPS = "coaps://";
- public static final String URI = COAP + host + ":" + port;
- public static final String SECURE_URI = COAPS + host + ":" + securityPort;
- public static final String URI_BS = COAP + hostBs + ":" + portBs;
- public static final String SECURE_URI_BS = COAPS + hostBs + ":" + securityPortBs;
+ public static final String URI = COAP + LWM2M_HOST + ":" + LWM2M_PORT;
+ public static final String SECURE_URI = COAPS + LWM2M_HOST + ":" + LWM2MS_PORT;
+ public static final String URI_BS = COAP + LWM2M_BOOTSTRAP_HOST + ":" + LWM2M_BOOTSTRAP_PORT;
+ public static final String SECURE_URI_BS = COAPS + LWM2M_BOOTSTRAP_HOST + ":" + LWM2MS_BOOTSTRAP_PORT;
public static final Security SECURITY_NO_SEC = noSec(URI, shortServerId);
protected final String OBSERVE_ATTRIBUTES_WITHOUT_PARAMS =
@@ -570,12 +586,11 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractTransportInte
this.clientDestroy();
lwM2MTestClient = new LwM2MTestClient(this.executor, endpoint, resources);
- try (ServerSocket socket = new ServerSocket(0)) {
- int clientPort = socket.getLocalPort();
- lwM2MTestClient.init(security, securityBs, clientPort, isRpc,
- this.defaultLwM2mUplinkMsgHandlerTest, this.clientContextTest,
- clientDtlsCidLength, queueMode, supportFormatOnly_SenMLJSON_SenMLCBOR, value3_0_9);
- }
+ int clientPort = PortFinder.findAvailableUdpPort();
+ //automatic client port assignment
+ lwM2MTestClient.init(security, securityBs, 0 /* clientPort */, isRpc,
+ this.defaultLwM2mUplinkMsgHandlerTest, this.clientContextTest,
+ clientDtlsCidLength, queueMode, supportFormatOnly_SenMLJSON_SenMLCBOR, value3_0_9);
lwM2MTestClient.setDeviceIdStr(deviceIdStr);
}
@@ -658,10 +673,9 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractTransportInte
try {
if (lwM2MTestClient != null && lwM2MTestClient.getLeshanClient() != null) {
boolean serverAlive = false;
- for (int port = AbstractLwM2MIntegrationTest.port; port <= securityPortBs; port++) {
- try (ServerSocket socket = new ServerSocket(port)) {
- log.info("Port {} is free.", port);
- } catch (IOException e) {
+ List ports = List.of(LWM2M_PORT, LWM2MS_PORT, LWM2MS_BOOTSTRAP_PORT, LWM2MS_BOOTSTRAP_PORT);
+ for (Integer port : ports) {
+ if (!PortFinder.isUDPPortAvailable(port)) {
log.debug("Port {} is busy — CoAP server still active.", port);
serverAlive = true;
break;
@@ -730,8 +744,8 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractTransportInte
bootstrapServerCredential.setServerPublicKey("");
bootstrapServerCredential.setShortServerId(isBootstrap ? null : shortServerId);
bootstrapServerCredential.setBootstrapServerIs(isBootstrap);
- bootstrapServerCredential.setHost(isBootstrap ? hostBs : host);
- bootstrapServerCredential.setPort(isBootstrap ? portBs : port);
+ bootstrapServerCredential.setHost(isBootstrap ? LWM2M_BOOTSTRAP_HOST : LWM2M_HOST);
+ bootstrapServerCredential.setPort(isBootstrap ? LWM2M_BOOTSTRAP_PORT : LWM2M_PORT);
return bootstrapServerCredential;
}
@@ -746,11 +760,15 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractTransportInte
return credentials;
}
-
protected void awaitObserveReadAll(int cntObserve, String deviceIdStr) throws Exception {
+ awaitObserveReadAll(cntObserve, deviceIdStr, "");
+ }
+
+ protected void awaitObserveReadAll(int cntObserve, String deviceIdStr, String assertionAlias) throws Exception {
try {
await("ObserveReadAll: countObserve " + cntObserve)
- .atMost(40, TimeUnit.SECONDS)
+ .alias(assertionAlias)
+ .atMost(TIMEOUT, TimeUnit.SECONDS)
.until(() -> cntObserve == getCntObserveAll(deviceIdStr));
} catch (ConditionTimeoutException e) {
int current = getCntObserveAll(deviceIdStr);
diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/FwLwM2MDevice.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/FwLwM2MDevice.java
index e2d57077a3..979a2b8c96 100644
--- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/FwLwM2MDevice.java
+++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/FwLwM2MDevice.java
@@ -33,7 +33,6 @@ import java.util.List;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.atomic.AtomicInteger;
import static org.thingsboard.server.dao.service.OtaPackageServiceTest.TARGET_FW_VERSION;
import static org.thingsboard.server.dao.service.OtaPackageServiceTest.TITLE;
@@ -137,6 +136,7 @@ public class FwLwM2MDevice extends BaseInstanceEnabler implements Destroyable {
@Override
public void destroy() {
scheduler.shutdownNow();
+ this.leshanClient = null;
}
private void startDownloading() {
diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2MTestClient.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2MTestClient.java
index bda2ef17e1..c9c39bc701 100644
--- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2MTestClient.java
+++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2MTestClient.java
@@ -33,7 +33,6 @@ import org.eclipse.leshan.client.object.Security;
import org.eclipse.leshan.client.object.Server;
import org.eclipse.leshan.client.observer.LwM2mClientObserver;
import org.eclipse.leshan.client.resource.DummyInstanceEnabler;
-import org.eclipse.leshan.client.resource.LwM2mInstanceEnabler;
import org.eclipse.leshan.client.resource.LwM2mObjectEnabler;
import org.eclipse.leshan.client.resource.ObjectsInitializer;
import org.eclipse.leshan.client.resource.listener.ObjectsListenerAdapter;
@@ -82,6 +81,7 @@ import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
+import static org.awaitility.Awaitility.await;
import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_CONNECTION_ID_LENGTH;
import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_RECOMMENDED_CIPHER_SUITES_ONLY;
import static org.eclipse.leshan.core.LwM2mId.ACCESS_CONTROL;
@@ -92,6 +92,7 @@ import static org.eclipse.leshan.core.LwM2mId.SECURITY;
import static org.eclipse.leshan.core.LwM2mId.SERVER;
import static org.eclipse.leshan.core.LwM2mId.SOFTWARE_MANAGEMENT;
import static org.eclipse.leshan.core.node.codec.DefaultLwM2mEncoder.getDefaultPathEncoder;
+import static org.thingsboard.server.transport.AbstractTransportIntegrationTest.DEFAULT_WAIT_TIMEOUT_SECONDS;
import static org.thingsboard.server.transport.lwm2m.AbstractLwM2MIntegrationTest.shortServerId;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.BINARY_APP_DATA_CONTAINER;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.LwM2MClientState;
@@ -119,6 +120,7 @@ import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.OBJECT_INST
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.TEMPERATURE_SENSOR;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.lwm2mClientResources;
import static org.thingsboard.server.transport.lwm2m.utils.LwM2MTransportUtil.setDtlsConnectorConfigCidLength;
+import static org.thingsboard.server.utils.PortFinder.isUDPPortAvailable;
@Slf4j
@@ -139,14 +141,16 @@ public class LwM2MTestClient {
private Map clientDtlsCid;
private LwM2mUplinkMsgHandler defaultLwM2mUplinkMsgHandlerTest;
private LwM2mClientContext clientContext;
- private LwM2mTemperatureSensor lwM2mTemperatureSensor12;
+ private LwM2mTemperatureSensor lwM2MTemperatureSensor12;
private String deviceIdStr;
+ private int clientPort;
- public void init(Security securityLwm2m, Security securityBs, int port, boolean isRpc,
+ public void init(Security securityLwm2m, Security securityBs, int clientPort, boolean isRpc,
LwM2mUplinkMsgHandler defaultLwM2mUplinkMsgHandler,
LwM2mClientContext clientContext, Integer cIdLength, boolean queueMode,
boolean supportFormatOnly_SenMLJSON_SenMLCBOR, Integer value3_0_9) throws InvalidDDFFileException, IOException {
Assert.assertNull("client already initialized", leshanClient);
+ this.clientPort = clientPort;
this.defaultLwM2mUplinkMsgHandlerTest = defaultLwM2mUplinkMsgHandler;
this.clientContext = clientContext;
@@ -156,7 +160,7 @@ public class LwM2MTestClient {
if (securityLwm2m != null && securityLwm2m.getId() != null) {
forceNullSecurityId(securityLwm2m);
}
- if (securityBs!= null && securityBs.getId() != null) {
+ if (securityBs != null && securityBs.getId() != null) {
forceNullSecurityId(securityBs);
}
if (securityBs != null && securityLwm2m != null) {
@@ -165,7 +169,7 @@ public class LwM2MTestClient {
} else if (securityBs != null) {
log.warn("Security BS only: securityBs: [{}] ", securityBs.getId());
initializer.setInstancesForObject(SECURITY, securityBs);
- } else if (securityLwm2m != null){
+ } else if (securityLwm2m != null) {
// SECURITY
log.warn("Security Lwm2m only: security Lwm2m [{}]", securityLwm2m.getId());
initializer.setInstancesForObject(SECURITY, securityLwm2m);
@@ -186,8 +190,8 @@ public class LwM2MTestClient {
locationParams.getPos();
initializer.setInstancesForObject(LOCATION, new LwM2mLocation(locationParams.getLatitude(), locationParams.getLongitude(), locationParams.getScaleFactor(), executor, OBJECT_INSTANCE_ID_0));
LwM2mTemperatureSensor lwM2mTemperatureSensor0 = new LwM2mTemperatureSensor(executor, OBJECT_INSTANCE_ID_0);
- lwM2mTemperatureSensor12 = new LwM2mTemperatureSensor(executor, OBJECT_INSTANCE_ID_12);
- initializer.setInstancesForObject(TEMPERATURE_SENSOR, lwM2mTemperatureSensor0, lwM2mTemperatureSensor12);
+ lwM2MTemperatureSensor12 = new LwM2mTemperatureSensor(executor, OBJECT_INSTANCE_ID_12);
+ initializer.setInstancesForObject(TEMPERATURE_SENSOR, lwM2mTemperatureSensor0, lwM2MTemperatureSensor12);
List enablers = initializer.createAll();
@@ -208,7 +212,9 @@ public class LwM2MTestClient {
builder.setSessionListener(new DtlsSessionLogger(clientStates, clientDtlsCid));
return builder;
- };
+ }
+
+ ;
};
}
};
@@ -245,7 +251,7 @@ public class LwM2MTestClient {
// Set Californium Configuration
endpointsBuilder.setConfiguration(clientCoapConfig);
- endpointsBuilder.setClientAddress(new InetSocketAddress(port).getAddress());
+ endpointsBuilder.setClientAddress(new InetSocketAddress(clientPort).getAddress());
// creates EndpointsProvider
@@ -438,12 +444,14 @@ public class LwM2MTestClient {
destroySafe(swLwM2MDevice);
destroySafe(lwM2MBinaryAppDataContainer);
destroySafe(lwM2MTemperatureSensor);
+ destroySafe(lwM2MTemperatureSensor12);
lwM2MDevice = null;
fwLwM2MDevice = null;
swLwM2MDevice = null;
lwM2MBinaryAppDataContainer = null;
lwM2MTemperatureSensor = null;
+ lwM2MTemperatureSensor12 = null;
}
@@ -462,11 +470,17 @@ public class LwM2MTestClient {
public void start(boolean isStartLw) {
if (leshanClient != null) {
+ if (clientPort > 0) {
+ log.error("Await UDP clientPort {} to be available before leshanClient.start()", clientPort);
+ await("Await UDP clientPort " + clientPort + " to be available before leshanClient.start()")
+ .atMost(DEFAULT_WAIT_TIMEOUT_SECONDS, TimeUnit.SECONDS)
+ .until(() -> isUDPPortAvailable(clientPort));
+ }
leshanClient.start();
if (isStartLw) {
this.awaitClientAfterStartConnectLw();
}
- lwM2mTemperatureSensor12.setLeshanClient(leshanClient);
+ lwM2MTemperatureSensor12.setLeshanClient(leshanClient);
fwLwM2MDevice.setLeshanClient(leshanClient);
}
}
@@ -474,6 +488,12 @@ public class LwM2MTestClient {
public void stop(boolean deregister) {
if (leshanClient != null) {
leshanClient.stop(deregister);
+ if (clientPort > 0) {
+ log.error("Await UDP clientPort {} to disconnect after leshanClient.stop(deregister)", clientPort);
+ await("Await client UDP port " + clientPort + " to disconnect after leshanClient.stop(deregister)")
+ .atMost(DEFAULT_WAIT_TIMEOUT_SECONDS, TimeUnit.SECONDS)
+ .until(() -> isUDPPortAvailable(clientPort));
+ }
}
}
@@ -535,5 +555,6 @@ public class LwM2MTestClient {
log.error("[forceNullSecurityId] Failed to set id=null for {}", security.getClass(), e);
}
}
+
}
diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2mTemperatureSensor.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2mTemperatureSensor.java
index 64978af44d..bca551e989 100644
--- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2mTemperatureSensor.java
+++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2mTemperatureSensor.java
@@ -169,6 +169,7 @@ public class LwM2mTemperatureSensor extends BaseInstanceEnabler implements Destr
@Override
public void destroy() {
+ this.leshanClient = null;
}
private void sendCollected() {
diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/AbstractSecurityLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/AbstractSecurityLwM2MIntegrationTest.java
index 20af793052..52bef912f2 100644
--- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/AbstractSecurityLwM2MIntegrationTest.java
+++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/AbstractSecurityLwM2MIntegrationTest.java
@@ -366,8 +366,8 @@ public abstract class AbstractSecurityLwM2MIntegrationTest extends AbstractLwM2M
}
bootstrapServerCredential.setShortServerId(isBootstrap ? null : shortServerId);
bootstrapServerCredential.setBootstrapServerIs(isBootstrap);
- bootstrapServerCredential.setHost(isBootstrap ? hostBs : host);
- bootstrapServerCredential.setPort(isBootstrap ? securityPortBs : securityPort);
+ bootstrapServerCredential.setHost(isBootstrap ? LWM2M_BOOTSTRAP_HOST : LWM2M_HOST);
+ bootstrapServerCredential.setPort(isBootstrap ? LWM2MS_BOOTSTRAP_PORT : LWM2MS_PORT);
return bootstrapServerCredential;
}
diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/PskLwm2mIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/PskLwm2mIntegrationTest.java
index 899fdc06a0..6083966837 100644
--- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/PskLwm2mIntegrationTest.java
+++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/PskLwm2mIntegrationTest.java
@@ -133,7 +133,7 @@ public class PskLwm2mIntegrationTest extends AbstractSecurityLwM2MIntegrationTes
ON_REGISTRATION_SUCCESS,
true);
- awaitObserveReadAll(1, lwm2mDevice.getId().getId().toString());
+ awaitObserveReadAll(1, lwm2mDevice.getId().getId().toString(), "before client stops for the first time");
lwM2MTestClient.stop(true);
DeviceProfile foundDeviceProfile = doGet("/api/deviceProfile/" + lwm2mDevice.getDeviceProfileId().getId().toString(), DeviceProfile.class);
@@ -143,7 +143,7 @@ public class PskLwm2mIntegrationTest extends AbstractSecurityLwM2MIntegrationTes
Assert.assertNotNull(lwm2mDeviceProfileManyParams);
lwM2MTestClient.start(true);
- awaitObserveReadAll(1, lwm2mDevice.getId().getId().toString());
+ awaitObserveReadAll(1, lwm2mDevice.getId().getId().toString(), "second after client restart");
awaitUpdateReg(3);
}
diff --git a/application/src/test/java/org/thingsboard/server/transport/mqtt/AbstractMqttIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/mqtt/AbstractMqttIntegrationTest.java
index 364b8fbe6a..962d85ec7c 100644
--- a/application/src/test/java/org/thingsboard/server/transport/mqtt/AbstractMqttIntegrationTest.java
+++ b/application/src/test/java/org/thingsboard/server/transport/mqtt/AbstractMqttIntegrationTest.java
@@ -19,7 +19,10 @@ import com.fasterxml.jackson.databind.node.ObjectNode;
import io.netty.handler.codec.mqtt.MqttQoS;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.paho.client.mqttv3.MqttException;
+import org.springframework.test.context.DynamicPropertyRegistry;
+import org.springframework.test.context.DynamicPropertySource;
import org.springframework.test.context.TestPropertySource;
+import org.springframework.test.util.TestSocketUtils;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceProfile;
@@ -58,6 +61,16 @@ import static org.junit.Assert.assertNotNull;
@Slf4j
public abstract class AbstractMqttIntegrationTest extends AbstractTransportIntegrationTest {
+ public static final String MQTT_HOST = "localhost";
+ public static final int MQTT_PORT = TestSocketUtils.findAvailableTcpPort();
+ public static final String MQTT_URL = "tcp://" + MQTT_HOST + ":" + MQTT_PORT;
+
+ @DynamicPropertySource
+ static void props(DynamicPropertyRegistry registry) {
+ log.warn("transport.mqtt.bind_port = {}", MQTT_PORT);
+ registry.add("transport.mqtt.bind_port", () -> MQTT_PORT);
+ }
+
protected Device savedGateway;
protected String gatewayAccessToken;
diff --git a/application/src/test/java/org/thingsboard/server/transport/mqtt/MqttGatewayRateLimitsTest.java b/application/src/test/java/org/thingsboard/server/transport/mqtt/MqttGatewayRateLimitsTest.java
index c455ae45d4..541ca40072 100644
--- a/application/src/test/java/org/thingsboard/server/transport/mqtt/MqttGatewayRateLimitsTest.java
+++ b/application/src/test/java/org/thingsboard/server/transport/mqtt/MqttGatewayRateLimitsTest.java
@@ -16,12 +16,15 @@
package org.thingsboard.server.transport.mqtt;
import com.fasterxml.jackson.databind.node.ObjectNode;
+import lombok.extern.slf4j.Slf4j;
import org.awaitility.Awaitility;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
import org.springframework.boot.test.mock.mockito.SpyBean;
+import org.springframework.test.context.DynamicPropertyRegistry;
+import org.springframework.test.context.DynamicPropertySource;
import org.springframework.test.context.TestPropertySource;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.Device;
@@ -43,13 +46,20 @@ import static org.junit.Assert.assertNotNull;
import static org.mockito.ArgumentMatchers.eq;
import static org.thingsboard.server.common.data.limit.LimitedApi.TRANSPORT_MESSAGES_PER_GATEWAY;
import static org.thingsboard.server.common.data.limit.LimitedApi.TRANSPORT_MESSAGES_PER_GATEWAY_DEVICE;
+import static org.thingsboard.server.transport.mqtt.AbstractMqttIntegrationTest.MQTT_PORT;
@DaoSqlTest
@TestPropertySource(properties = {
"service.integrations.supported=ALL",
"transport.mqtt.enabled=true",
})
+@Slf4j
public class MqttGatewayRateLimitsTest extends AbstractControllerTest {
+ @DynamicPropertySource
+ static void props(DynamicPropertyRegistry registry) {
+ log.warn("transport.mqtt.bind_port = {}", MQTT_PORT);
+ registry.add("transport.mqtt.bind_port", () -> MQTT_PORT);
+ }
private static final String GATEWAY_TOPIC = "v1/gateway/telemetry";
private static final String DEVICE_TOPIC = "v1/devices/me/telemetry";
diff --git a/application/src/test/java/org/thingsboard/server/transport/mqtt/mqttv3/MqttTestClient.java b/application/src/test/java/org/thingsboard/server/transport/mqtt/mqttv3/MqttTestClient.java
index 7e5cc031a5..4c34d9bc81 100644
--- a/application/src/test/java/org/thingsboard/server/transport/mqtt/mqttv3/MqttTestClient.java
+++ b/application/src/test/java/org/thingsboard/server/transport/mqtt/mqttv3/MqttTestClient.java
@@ -27,12 +27,16 @@ import org.thingsboard.server.common.data.StringUtils;
import java.util.concurrent.TimeUnit;
+import static org.thingsboard.server.transport.mqtt.AbstractMqttIntegrationTest.MQTT_HOST;
+import static org.thingsboard.server.transport.mqtt.AbstractMqttIntegrationTest.MQTT_PORT;
+
public class MqttTestClient {
- private static final String MQTT_URL = "tcp://localhost:1883";
private static final int TIMEOUT = 30; // seconds
public static final long TIMEOUT_MS = TimeUnit.SECONDS.toMillis(TIMEOUT);
+ private String MQTT_URL = "tcp://" + MQTT_HOST + ":" + MQTT_PORT;
+
private final MqttAsyncClient client;
public void setCallback(MqttTestCallback callback) {
diff --git a/application/src/test/java/org/thingsboard/server/transport/mqtt/mqttv5/MqttV5TestClient.java b/application/src/test/java/org/thingsboard/server/transport/mqtt/mqttv5/MqttV5TestClient.java
index 545fc148e2..558fa1a46a 100644
--- a/application/src/test/java/org/thingsboard/server/transport/mqtt/mqttv5/MqttV5TestClient.java
+++ b/application/src/test/java/org/thingsboard/server/transport/mqtt/mqttv5/MqttV5TestClient.java
@@ -27,12 +27,16 @@ import org.thingsboard.server.common.data.StringUtils;
import java.util.concurrent.TimeUnit;
+import static org.thingsboard.server.transport.mqtt.AbstractMqttIntegrationTest.MQTT_HOST;
+import static org.thingsboard.server.transport.mqtt.AbstractMqttIntegrationTest.MQTT_PORT;
+
public class MqttV5TestClient { // We should copy part of MqttV3TestClient, due to different package names in import
- private static final String MQTT_URL = "tcp://localhost:1883";
private static final int TIMEOUT = 30; // seconds
private static final long TIMEOUT_MS = TimeUnit.SECONDS.toMillis(TIMEOUT);
+ private String MQTT_URL = "tcp://" + MQTT_HOST + ":" + MQTT_PORT;
+
private final MqttAsyncClient client;
public void setCallback(MqttCallback callback) {
diff --git a/application/src/test/java/org/thingsboard/server/utils/PortFinder.java b/application/src/test/java/org/thingsboard/server/utils/PortFinder.java
new file mode 100644
index 0000000000..21a1f1eb0d
--- /dev/null
+++ b/application/src/test/java/org/thingsboard/server/utils/PortFinder.java
@@ -0,0 +1,42 @@
+/**
+ * Copyright © 2016-2026 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.utils;
+
+import lombok.extern.slf4j.Slf4j;
+
+import java.io.IOException;
+import java.net.DatagramSocket;
+import java.net.SocketException;
+
+@Slf4j
+public class PortFinder {
+ public static int findAvailableUdpPort() {
+ try (DatagramSocket socket = new DatagramSocket(0)) {
+ return socket.getLocalPort();
+ } catch (SocketException e) {
+ throw new IllegalStateException("No available UDP ports found", e);
+ }
+ }
+
+ public static boolean isUDPPortAvailable(int port) {
+ try (DatagramSocket socket = new DatagramSocket(port)) {
+ return true;
+ } catch (IOException e) {
+ log.debug("Failed to open UDP port {}", port, e);
+ return false;
+ }
+ }
+}
diff --git a/application/src/test/resources/application-test.properties b/application/src/test/resources/application-test.properties
index b8bdcf67e6..e79289340c 100644
--- a/application/src/test/resources/application-test.properties
+++ b/application/src/test/resources/application-test.properties
@@ -1,3 +1,4 @@
+spring.main.banner-mode=off
js.evaluator=mock
transport.lwm2m.server.security.credentials.enabled=true
transport.lwm2m.server.security.credentials.type=KEYSTORE
diff --git a/application/src/test/resources/logback-test.xml b/application/src/test/resources/logback-test.xml
index 13c93da411..6faaf97536 100644
--- a/application/src/test/resources/logback-test.xml
+++ b/application/src/test/resources/logback-test.xml
@@ -42,6 +42,12 @@
+
+
+
+
+
+
diff --git a/common/actor/pom.xml b/common/actor/pom.xml
index bd441e3a58..6326c45685 100644
--- a/common/actor/pom.xml
+++ b/common/actor/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 4.3.0-RC
+ 4.4.0-SNAPSHOT
common
org.thingsboard.common
diff --git a/common/cache/pom.xml b/common/cache/pom.xml
index 7d22f5d3dc..774015fc33 100644
--- a/common/cache/pom.xml
+++ b/common/cache/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 4.3.0-RC
+ 4.4.0-SNAPSHOT
common
org.thingsboard.common
diff --git a/common/cache/src/test/resources/logback-test.xml b/common/cache/src/test/resources/logback-test.xml
index 29dddea8df..9b4c5b62df 100644
--- a/common/cache/src/test/resources/logback-test.xml
+++ b/common/cache/src/test/resources/logback-test.xml
@@ -9,6 +9,9 @@
+
+
+
diff --git a/common/cluster-api/pom.xml b/common/cluster-api/pom.xml
index a7240c8b42..5983b96b95 100644
--- a/common/cluster-api/pom.xml
+++ b/common/cluster-api/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 4.3.0-RC
+ 4.4.0-SNAPSHOT
common
org.thingsboard.common
diff --git a/common/coap-server/pom.xml b/common/coap-server/pom.xml
index 3fba2211d5..32824826c3 100644
--- a/common/coap-server/pom.xml
+++ b/common/coap-server/pom.xml
@@ -22,7 +22,7 @@
4.0.0
org.thingsboard
- 4.3.0-RC
+ 4.4.0-SNAPSHOT
common
org.thingsboard.common
diff --git a/common/dao-api/pom.xml b/common/dao-api/pom.xml
index bcab0249ef..12533cd813 100644
--- a/common/dao-api/pom.xml
+++ b/common/dao-api/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 4.3.0-RC
+ 4.4.0-SNAPSHOT
common
org.thingsboard.common
diff --git a/common/data/pom.xml b/common/data/pom.xml
index 43f3a5a38e..0647e706eb 100644
--- a/common/data/pom.xml
+++ b/common/data/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 4.3.0-RC
+ 4.4.0-SNAPSHOT
common
org.thingsboard.common
diff --git a/common/discovery-api/pom.xml b/common/discovery-api/pom.xml
index 5aa66562ed..c7658bd738 100644
--- a/common/discovery-api/pom.xml
+++ b/common/discovery-api/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 4.3.0-RC
+ 4.4.0-SNAPSHOT
common
org.thingsboard.common
diff --git a/common/edge-api/pom.xml b/common/edge-api/pom.xml
index 507bfc73f8..b5d7b777da 100644
--- a/common/edge-api/pom.xml
+++ b/common/edge-api/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 4.3.0-RC
+ 4.4.0-SNAPSHOT
common
org.thingsboard.common
diff --git a/common/edqs/pom.xml b/common/edqs/pom.xml
index 5512d47b79..549ee1515f 100644
--- a/common/edqs/pom.xml
+++ b/common/edqs/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 4.3.0-RC
+ 4.4.0-SNAPSHOT
common
org.thingsboard.common
diff --git a/common/edqs/src/main/java/org/thingsboard/server/edqs/util/TbRocksDb.java b/common/edqs/src/main/java/org/thingsboard/server/edqs/util/TbRocksDb.java
index 9141321dcb..c731cf4548 100644
--- a/common/edqs/src/main/java/org/thingsboard/server/edqs/util/TbRocksDb.java
+++ b/common/edqs/src/main/java/org/thingsboard/server/edqs/util/TbRocksDb.java
@@ -16,6 +16,7 @@
package org.thingsboard.server.edqs.util;
import lombok.SneakyThrows;
+import lombok.extern.slf4j.Slf4j;
import org.rocksdb.Options;
import org.rocksdb.RocksDB;
import org.rocksdb.RocksIterator;
@@ -26,6 +27,7 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.util.function.BiConsumer;
+@Slf4j
public class TbRocksDb {
protected final String path;
@@ -45,6 +47,7 @@ public class TbRocksDb {
@SneakyThrows
public void init() {
+ log.debug("RocksDB init in {}", path);
Files.createDirectories(Path.of(path).getParent());
db = RocksDB.open(dbOptions, path);
}
diff --git a/common/message/pom.xml b/common/message/pom.xml
index 1f6bfb20e1..478368afdc 100644
--- a/common/message/pom.xml
+++ b/common/message/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 4.3.0-RC
+ 4.4.0-SNAPSHOT
common
org.thingsboard.common
diff --git a/common/pom.xml b/common/pom.xml
index c0140e733d..d241646816 100644
--- a/common/pom.xml
+++ b/common/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 4.3.0-RC
+ 4.4.0-SNAPSHOT
thingsboard
common
diff --git a/common/proto/pom.xml b/common/proto/pom.xml
index 27ca58ac0e..e24705c25a 100644
--- a/common/proto/pom.xml
+++ b/common/proto/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 4.3.0-RC
+ 4.4.0-SNAPSHOT
common
org.thingsboard.common
diff --git a/common/queue/pom.xml b/common/queue/pom.xml
index b4fd04ef55..1cb159b3af 100644
--- a/common/queue/pom.xml
+++ b/common/queue/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 4.3.0-RC
+ 4.4.0-SNAPSHOT
common
org.thingsboard.common
diff --git a/common/script/pom.xml b/common/script/pom.xml
index 2a5856782e..a11d320416 100644
--- a/common/script/pom.xml
+++ b/common/script/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 4.3.0-RC
+ 4.4.0-SNAPSHOT
common
org.thingsboard.common
diff --git a/common/script/remote-js-client/pom.xml b/common/script/remote-js-client/pom.xml
index 6a50189371..202cc20bd3 100644
--- a/common/script/remote-js-client/pom.xml
+++ b/common/script/remote-js-client/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard.common
- 4.3.0-RC
+ 4.4.0-SNAPSHOT
script
org.thingsboard.common.script
diff --git a/common/script/script-api/pom.xml b/common/script/script-api/pom.xml
index 38e32661d1..b074db321a 100644
--- a/common/script/script-api/pom.xml
+++ b/common/script/script-api/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard.common
- 4.3.0-RC
+ 4.4.0-SNAPSHOT
script
org.thingsboard.common.script
diff --git a/common/stats/pom.xml b/common/stats/pom.xml
index fe35fedaec..da650623f9 100644
--- a/common/stats/pom.xml
+++ b/common/stats/pom.xml
@@ -22,7 +22,7 @@
4.0.0
org.thingsboard
- 4.3.0-RC
+ 4.4.0-SNAPSHOT
common
org.thingsboard.common
diff --git a/common/transport/coap/pom.xml b/common/transport/coap/pom.xml
index bb8213666f..76ac33b450 100644
--- a/common/transport/coap/pom.xml
+++ b/common/transport/coap/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard.common
- 4.3.0-RC
+ 4.4.0-SNAPSHOT
transport
org.thingsboard.common.transport
diff --git a/common/transport/http/pom.xml b/common/transport/http/pom.xml
index d341540104..275a497b6e 100644
--- a/common/transport/http/pom.xml
+++ b/common/transport/http/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard.common
- 4.3.0-RC
+ 4.4.0-SNAPSHOT
transport
org.thingsboard.common.transport
diff --git a/common/transport/lwm2m/pom.xml b/common/transport/lwm2m/pom.xml
index cd579c9011..f47255ac7e 100644
--- a/common/transport/lwm2m/pom.xml
+++ b/common/transport/lwm2m/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard.common
- 4.3.0-RC
+ 4.4.0-SNAPSHOT
transport
org.thingsboard.common.transport
diff --git a/common/transport/mqtt/pom.xml b/common/transport/mqtt/pom.xml
index a1e13b9d37..985c2a82f6 100644
--- a/common/transport/mqtt/pom.xml
+++ b/common/transport/mqtt/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard.common
- 4.3.0-RC
+ 4.4.0-SNAPSHOT
transport
org.thingsboard.common.transport
diff --git a/common/transport/pom.xml b/common/transport/pom.xml
index 697874ea7e..810b22670a 100644
--- a/common/transport/pom.xml
+++ b/common/transport/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 4.3.0-RC
+ 4.4.0-SNAPSHOT
common
org.thingsboard.common
diff --git a/common/transport/snmp/pom.xml b/common/transport/snmp/pom.xml
index 011c01a453..f98b2426dc 100644
--- a/common/transport/snmp/pom.xml
+++ b/common/transport/snmp/pom.xml
@@ -21,7 +21,7 @@
org.thingsboard.common
- 4.3.0-RC
+ 4.4.0-SNAPSHOT
transport
diff --git a/common/transport/transport-api/pom.xml b/common/transport/transport-api/pom.xml
index 50a0a07b03..73e0bdeec0 100644
--- a/common/transport/transport-api/pom.xml
+++ b/common/transport/transport-api/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard.common
- 4.3.0-RC
+ 4.4.0-SNAPSHOT
transport
org.thingsboard.common.transport
diff --git a/common/util/pom.xml b/common/util/pom.xml
index c540f756a7..f3077d49c3 100644
--- a/common/util/pom.xml
+++ b/common/util/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 4.3.0-RC
+ 4.4.0-SNAPSHOT
common
org.thingsboard.common
diff --git a/common/version-control/pom.xml b/common/version-control/pom.xml
index ce7102d3f2..d9e134128d 100644
--- a/common/version-control/pom.xml
+++ b/common/version-control/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 4.3.0-RC
+ 4.4.0-SNAPSHOT
common
org.thingsboard.common
diff --git a/dao/pom.xml b/dao/pom.xml
index 4ec8d13fd0..928a107079 100644
--- a/dao/pom.xml
+++ b/dao/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 4.3.0-RC
+ 4.4.0-SNAPSHOT
thingsboard
dao
diff --git a/dao/src/main/java/org/thingsboard/server/dao/resource/DefaultTbResourceDataCache.java b/dao/src/main/java/org/thingsboard/server/dao/resource/DefaultTbResourceDataCache.java
index 343c37299e..925b8c2912 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/resource/DefaultTbResourceDataCache.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/resource/DefaultTbResourceDataCache.java
@@ -19,6 +19,7 @@ import com.github.benmanes.caffeine.cache.AsyncLoadingCache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.google.common.util.concurrent.FluentFuture;
import jakarta.annotation.PostConstruct;
+import jakarta.annotation.PreDestroy;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
@@ -55,6 +56,12 @@ public class DefaultTbResourceDataCache implements TbResourceDataCache {
.buildAsync((key, executor) -> CompletableFuture.supplyAsync(() -> resourceService.getResourceDataInfo(key.tenantId(), key.resourceId()), executor));
}
+ @PreDestroy
+ private void destroy() {
+ cache.synchronous().invalidateAll();
+ cache = null;
+ }
+
@Override
public FluentFuture getResourceDataInfoAsync(TenantId tenantId, TbResourceId resourceId) {
log.trace("Retrieving resource data info by id [{}], tenant id [{}] from cache", resourceId, tenantId);
diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/DeviceProfileDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/DeviceProfileDataValidator.java
index 8006793b87..74fa56dc87 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/service/validator/DeviceProfileDataValidator.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/DeviceProfileDataValidator.java
@@ -100,6 +100,18 @@ public class DeviceProfileDataValidator extends AbstractHasOtaPackageValidator findProfileEntityIdInfosByTenantId(UUID tenantId, Pageable pageable) {
String PROFILE_ASSET_ID_INFO_QUERY = String.format("SELECT tenant_id as tenantId, customer_id as customerId, asset_profile_id as profileId, id as id FROM asset WHERE tenant_id = '%s' ORDER BY created_time ASC LIMIT %%s OFFSET %%s", tenantId);
- return find(COUNT_QUERY, PROFILE_ASSET_ID_INFO_QUERY, pageable, DefaultNativeAssetRepository::toInfo);
+ String COUNT_QUERY_BY_TENANT = String.format("SELECT count(id) FROM asset WHERE tenant_id = '%s';", tenantId);
+ return find(COUNT_QUERY_BY_TENANT, PROFILE_ASSET_ID_INFO_QUERY, pageable, DefaultNativeAssetRepository::toInfo);
}
private static ProfileEntityIdInfo toInfo(Map row) {
diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/device/DefaultNativeDeviceRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/device/DefaultNativeDeviceRepository.java
index 275729f1e9..2a15aee984 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/sql/device/DefaultNativeDeviceRepository.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/sql/device/DefaultNativeDeviceRepository.java
@@ -62,7 +62,8 @@ public class DefaultNativeDeviceRepository extends AbstractNativeRepository impl
@Override
public PageData findProfileEntityIdInfosByTenantId(UUID tenantId, Pageable pageable) {
String PROFILE_DEVICE_ID_INFO_QUERY = String.format("SELECT tenant_id as tenantId, customer_id as customerId, device_profile_id as profileId, id as id FROM device WHERE tenant_id = '%s' ORDER BY created_time ASC LIMIT %%s OFFSET %%s", tenantId);
- return find(COUNT_QUERY, PROFILE_DEVICE_ID_INFO_QUERY, pageable, DefaultNativeDeviceRepository::toInfo);
+ String COUNT_QUERY_BY_TENANT = String.format("SELECT count(id) FROM device WHERE tenant_id = '%s';", tenantId);
+ return find(COUNT_QUERY_BY_TENANT, PROFILE_DEVICE_ID_INFO_QUERY, pageable, DefaultNativeDeviceRepository::toInfo);
}
private static ProfileEntityIdInfo toInfo(Map row) {
diff --git a/dao/src/test/resources/application-test.properties b/dao/src/test/resources/application-test.properties
index de4b342af5..821317b6b4 100644
--- a/dao/src/test/resources/application-test.properties
+++ b/dao/src/test/resources/application-test.properties
@@ -1,3 +1,4 @@
+spring.main.banner-mode=off
zk.enabled=false
zk.url=localhost:2181
zk.zk_dir=/thingsboard
diff --git a/dao/src/test/resources/logback-test.xml b/dao/src/test/resources/logback-test.xml
index fe0887c678..92cd10f2ca 100644
--- a/dao/src/test/resources/logback-test.xml
+++ b/dao/src/test/resources/logback-test.xml
@@ -19,6 +19,9 @@
+
+
+
diff --git a/edqs/pom.xml b/edqs/pom.xml
index 25c6125d82..1081b89276 100644
--- a/edqs/pom.xml
+++ b/edqs/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 4.3.0-RC
+ 4.4.0-SNAPSHOT
thingsboard
edqs
diff --git a/monitoring/pom.xml b/monitoring/pom.xml
index a1addced5d..391347e56b 100644
--- a/monitoring/pom.xml
+++ b/monitoring/pom.xml
@@ -21,7 +21,7 @@
4.0.0
org.thingsboard
- 4.3.0-RC
+ 4.4.0-SNAPSHOT
thingsboard
diff --git a/msa/black-box-tests/pom.xml b/msa/black-box-tests/pom.xml
index 425948ae43..b08d05ed35 100644
--- a/msa/black-box-tests/pom.xml
+++ b/msa/black-box-tests/pom.xml
@@ -21,7 +21,7 @@
org.thingsboard
- 4.3.0-RC
+ 4.4.0-SNAPSHOT
msa
org.thingsboard.msa
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/AbstractCoapClientTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/AbstractCoapClientTest.java
index d4d9d08474..e409a82600 100644
--- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/AbstractCoapClientTest.java
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/AbstractCoapClientTest.java
@@ -28,7 +28,7 @@ import org.thingsboard.server.common.msg.session.FeatureType;
public abstract class AbstractCoapClientTest extends AbstractContainerTest{
- private static final String COAP_BASE_URL = "coap://localhost:5683/api/v1/";
+ private static final String CONTAINER_COAP_BASE_URL = "coap://localhost:5683/api/v1/";
private static final long CLIENT_REQUEST_TIMEOUT = 60000L;
@@ -54,7 +54,7 @@ public abstract class AbstractCoapClientTest extends AbstractContainerTest{
protected byte[] createCoapClientAndPublish(String deviceName) throws Exception {
String provisionRequestMsg = createTestProvisionMessage(deviceName);
Configuration.addDefaultModule(MODULE_DEFINITIONS_PROVIDER);
- String featureTokenUrl = COAP_BASE_URL + FeatureType.PROVISION.name().toLowerCase();
+ String featureTokenUrl = CONTAINER_COAP_BASE_URL + FeatureType.PROVISION.name().toLowerCase();
client = new CoapClient(featureTokenUrl);
return client.setTimeout(CLIENT_REQUEST_TIMEOUT)
.post(provisionRequestMsg.getBytes(), MediaTypeRegistry.APPLICATION_JSON)
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/AbstractContainerTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/AbstractContainerTest.java
index ebf90bb7f0..ec1bdcf658 100644
--- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/AbstractContainerTest.java
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/AbstractContainerTest.java
@@ -44,7 +44,6 @@ import java.util.Map;
import java.util.Random;
import java.util.function.Consumer;
-
@Slf4j
@Listeners(TestListener.class)
public abstract class AbstractContainerTest {
@@ -170,19 +169,14 @@ public abstract class AbstractContainerTest {
DeviceProfileProvisionConfiguration provisionConfiguration;
String testProvisionDeviceKey = TEST_PROVISION_DEVICE_KEY;
deviceProfile.setProvisionType(provisionType);
- switch(provisionType) {
- case ALLOW_CREATE_NEW_DEVICES:
- provisionConfiguration = new AllowCreateNewDevicesDeviceProfileProvisionConfiguration(TEST_PROVISION_DEVICE_SECRET);
- break;
- case CHECK_PRE_PROVISIONED_DEVICES:
- provisionConfiguration = new CheckPreProvisionedDevicesDeviceProfileProvisionConfiguration(TEST_PROVISION_DEVICE_SECRET);
- break;
- default:
- case DISABLED:
+ provisionConfiguration = switch (provisionType) {
+ case ALLOW_CREATE_NEW_DEVICES -> new AllowCreateNewDevicesDeviceProfileProvisionConfiguration(TEST_PROVISION_DEVICE_SECRET);
+ case CHECK_PRE_PROVISIONED_DEVICES -> new CheckPreProvisionedDevicesDeviceProfileProvisionConfiguration(TEST_PROVISION_DEVICE_SECRET);
+ default -> {
testProvisionDeviceKey = null;
- provisionConfiguration = new DisabledDeviceProfileProvisionConfiguration(null);
- break;
- }
+ yield new DisabledDeviceProfileProvisionConfiguration(null);
+ }
+ };
DeviceProfileData deviceProfileData = deviceProfile.getProfileData();
deviceProfileData.setProvisionConfiguration(provisionConfiguration);
deviceProfile.setProfileData(deviceProfileData);
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/PortFinder.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/PortFinder.java
new file mode 100644
index 0000000000..8bd036bf15
--- /dev/null
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/PortFinder.java
@@ -0,0 +1,42 @@
+/**
+ * Copyright © 2016-2026 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.msa;
+
+import lombok.extern.slf4j.Slf4j;
+
+import java.io.IOException;
+import java.net.DatagramSocket;
+import java.net.SocketException;
+
+@Slf4j
+public class PortFinder {
+ public static int findAvailableUdpPort() {
+ try (DatagramSocket socket = new DatagramSocket(0)) {
+ return socket.getLocalPort();
+ } catch (SocketException e) {
+ throw new IllegalStateException("No available UDP ports found", e);
+ }
+ }
+
+ public static boolean isUDPPortAvailable(int port) {
+ try (DatagramSocket socket = new DatagramSocket(port)) {
+ return true;
+ } catch (IOException e) {
+ log.debug("Failed to open UDP port {}", port, e);
+ return false;
+ }
+ }
+}
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/JavaRestClientTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/JavaRestClientTest.java
index c9ebfad3e2..24e7e78039 100644
--- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/JavaRestClientTest.java
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/JavaRestClientTest.java
@@ -16,6 +16,7 @@
package org.thingsboard.server.msa.connectivity;
import com.google.gson.JsonObject;
+import org.apache.commons.lang3.RandomStringUtils;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder;
@@ -26,7 +27,6 @@ import org.apache.hc.client5.http.ssl.NoopHostnameVerifier;
import org.apache.hc.core5.ssl.SSLContexts;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
-import org.testcontainers.shaded.org.apache.commons.lang3.RandomStringUtils;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.BeforeMethod;
@@ -34,6 +34,10 @@ import org.testng.annotations.Test;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.rest.client.RestClient;
import org.thingsboard.server.common.data.Device;
+import org.thingsboard.server.common.data.DeviceProfile;
+import org.thingsboard.server.common.data.DeviceProfileInfo;
+import org.thingsboard.server.common.data.DeviceProfileType;
+import org.thingsboard.server.common.data.DeviceTransportType;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.User;
@@ -41,6 +45,11 @@ import org.thingsboard.server.common.data.alarm.Alarm;
import org.thingsboard.server.common.data.alarm.AlarmInfo;
import org.thingsboard.server.common.data.alarm.AlarmSearchStatus;
import org.thingsboard.server.common.data.alarm.AlarmSeverity;
+import org.thingsboard.server.common.data.asset.AssetProfile;
+import org.thingsboard.server.common.data.asset.AssetProfileInfo;
+import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileConfiguration;
+import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileTransportConfiguration;
+import org.thingsboard.server.common.data.device.profile.DeviceProfileData;
import org.thingsboard.server.common.data.domain.Domain;
import org.thingsboard.server.common.data.domain.DomainInfo;
import org.thingsboard.server.common.data.id.NotificationTargetId;
@@ -94,6 +103,7 @@ import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -109,8 +119,10 @@ public class JavaRestClientTest extends AbstractContainerTest {
public static final String DEFAULT_NOTIFICATION_SUBJECT = "Just a test";
public static final NotificationType DEFAULT_NOTIFICATION_TYPE = NotificationType.GENERAL;
private RestClient restClient;
- private Tenant tenant;
- private User user;
+ private Tenant tenant1;
+ private Tenant tenant2;
+ private User tenantAdmin1;
+ private User tenantAdmin2;
@BeforeClass
public void beforeClass() throws Exception {
@@ -140,31 +152,45 @@ public class JavaRestClientTest extends AbstractContainerTest {
public void setUp() throws Exception {
restClient.login("sysadmin@thingsboard.org", "sysadmin");
- // create tenant and tenant admin
- tenant = new Tenant();
- tenant.setTitle("Java Rest Client Test Tenant " + RandomStringUtils.randomAlphabetic(5));
- tenant = restClient.saveTenant(tenant);
+ // create tenant 1 and tenant admin 1
+ tenant1 = new Tenant();
+ tenant1.setTitle("Java Rest Client Test Tenant " + RandomStringUtils.insecure().randomAlphabetic(5));
+ tenant1 = restClient.saveTenant(tenant1);
- String email = RandomStringUtils.randomAlphabetic(5) + "@gmail.com";
- user = restClient.saveUser(defaultTenantAdmin(tenant.getId(), email), false);
- restClient.activateUser(user.getId(), "password123", false);
- restClient.login(email, "password123");
+ String email1 = RandomStringUtils.insecure().randomAlphabetic(5) + "@gmail.com";
+ tenantAdmin1 = restClient.saveUser(defaultTenantAdmin(tenant1.getId(), email1), false);
+ restClient.activateUser(tenantAdmin1.getId(), "password123", false);
+
+ // create tenant 2 and tenant admin 2
+ tenant2 = new Tenant();
+ tenant2.setTitle("Java Rest Client Test Tenant " + RandomStringUtils.insecure().randomAlphabetic(5));
+ tenant2 = restClient.saveTenant(tenant2);
+
+ String email2 = RandomStringUtils.insecure().randomAlphabetic(5) + "@gmail.com";
+ tenantAdmin2 = restClient.saveUser(defaultTenantAdmin(tenant2.getId(), email2), false);
+ restClient.activateUser(tenantAdmin2.getId(), "password123", false);
+
+ // tenant 1 tenant admin by default
+ restClient.login(tenantAdmin1.getEmail(), "password123");
}
@AfterMethod
public void tearDown() {
restClient.login("sysadmin@thingsboard.org", "sysadmin");
- if (tenant != null) {
- restClient.deleteTenant(tenant.getId());
+ if (tenant1 != null) {
+ restClient.deleteTenant(tenant1.getId());
+ }
+ if (tenant2 != null) {
+ restClient.deleteTenant(tenant2.getId());
}
}
@Test
public void testGetAlarmsV2() {
- Device device = restClient.saveDevice(defaultDevicePrototype(RandomStringUtils.randomAlphabetic(5)));
+ Device device = restClient.saveDevice(defaultDevicePrototype(RandomStringUtils.insecure().randomAlphabetic(5)));
assertThat(device).isNotNull();
- String type = "High temp" + RandomStringUtils.randomAlphabetic(5);
+ String type = "High temp" + RandomStringUtils.insecure().randomAlphabetic(5);
Alarm alarm = Alarm.builder()
.originator(device.getId())
.severity(AlarmSeverity.CRITICAL)
@@ -202,7 +228,7 @@ public class JavaRestClientTest extends AbstractContainerTest {
@Test
public void testTimeSeriesByReadTsKvQueries() {
- Device device = restClient.saveDevice(defaultDevicePrototype(RandomStringUtils.randomAlphabetic(5)));
+ Device device = restClient.saveDevice(defaultDevicePrototype(RandomStringUtils.insecure().randomAlphabetic(5)));
assertThat(device).isNotNull();
DeviceCredentials deviceCredentials = restClient.getDeviceCredentialsByDeviceId(device.getId()).get();
@@ -230,7 +256,7 @@ public class JavaRestClientTest extends AbstractContainerTest {
@Test
public void testFindNotifications() {
- NotificationTarget notificationTarget = createNotificationTarget(user.getId());
+ NotificationTarget notificationTarget = createNotificationTarget(tenantAdmin1.getId());
String notificationText1 = "Notification 1";
NotificationTemplate notificationTemplate = createNotificationTemplate(DEFAULT_NOTIFICATION_TYPE, DEFAULT_NOTIFICATION_SUBJECT, notificationText1, new NotificationDeliveryMethod[]{WEB});
NotificationRequest notificationRequest = submitNotificationRequest(notificationTarget.getId(), notificationTemplate.getId());
@@ -248,7 +274,7 @@ public class JavaRestClientTest extends AbstractContainerTest {
NotificationRequestPreview requestPreview = restClient.getNotificationRequestPreview(notificationRequest, 10);
assertThat(requestPreview.getTotalRecipientsCount()).isEqualTo(1);
- assertThat(requestPreview.getRecipientsPreview()).isEqualTo(List.of(user.getEmail()));
+ assertThat(requestPreview.getRecipientsPreview()).isEqualTo(List.of(tenantAdmin1.getEmail()));
PageData notifications = restClient.getNotifications(false, WEB, new PageLink(30));
assertThat(notifications.getTotalElements()).isEqualTo(2);
@@ -316,7 +342,7 @@ public class JavaRestClientTest extends AbstractContainerTest {
restClient.login("sysadmin@thingsboard.org", "sysadmin");
Domain domain = new Domain();
- String prefix = RandomStringUtils.randomAlphabetic(5).toLowerCase();
+ String prefix = RandomStringUtils.insecure().randomAlphabetic(5).toLowerCase();
domain.setName(prefix + ".test.com");
Domain savedDomain = restClient.saveDomain(domain);
assertThat(savedDomain.getName()).isEqualTo(domain.getName());
@@ -330,10 +356,10 @@ public class JavaRestClientTest extends AbstractContainerTest {
restClient.login("sysadmin@thingsboard.org", "sysadmin");
MobileApp mobileApp = new MobileApp();
- String prefix = RandomStringUtils.randomAlphabetic(5).toLowerCase();
+ String prefix = RandomStringUtils.insecure().randomAlphabetic(5).toLowerCase();
mobileApp.setPkgName(prefix + "test.app.apple");
mobileApp.setPlatformType(PlatformType.ANDROID);
- mobileApp.setAppSecret(RandomStringUtils.randomAlphabetic(20));
+ mobileApp.setAppSecret(RandomStringUtils.insecure().randomAlphabetic(20));
mobileApp.setStatus(MobileAppStatus.DRAFT);
MobileApp savedMobileApp = restClient.saveMobileApp(mobileApp);
@@ -343,7 +369,7 @@ public class JavaRestClientTest extends AbstractContainerTest {
assertThat(retrieved.getData()).hasSize(1);
MobileAppBundle mobileAppBundle = new MobileAppBundle();
- String bundlePrefix = RandomStringUtils.randomAlphabetic(5).toLowerCase();
+ String bundlePrefix = RandomStringUtils.insecure().randomAlphabetic(5).toLowerCase();
mobileAppBundle.setTitle(bundlePrefix + "Test Bundle");
mobileAppBundle.setAndroidAppId(savedMobileApp.getId());
@@ -357,7 +383,7 @@ public class JavaRestClientTest extends AbstractContainerTest {
filter.setUsersIds(Arrays.stream(usersIds).map(UUIDBased::getId).toList());
NotificationTarget notificationTarget = new NotificationTarget();
- notificationTarget.setName(filter + RandomStringUtils.randomNumeric(5));
+ notificationTarget.setName(filter + RandomStringUtils.insecure().randomNumeric(5));
PlatformUsersNotificationTargetConfig targetConfig = new PlatformUsersNotificationTargetConfig();
targetConfig.setUsersFilter(filter);
notificationTarget.setConfiguration(targetConfig);
@@ -367,7 +393,7 @@ public class JavaRestClientTest extends AbstractContainerTest {
private NotificationTemplate createNotificationTemplate(NotificationType notificationType, String subject,
String text, NotificationDeliveryMethod... deliveryMethods) {
NotificationTemplate notificationTemplate = new NotificationTemplate();
- notificationTemplate.setName("Notification template: " + RandomStringUtils.randomAlphabetic(5));
+ notificationTemplate.setName("Notification template: " + RandomStringUtils.insecure().randomAlphabetic(5));
notificationTemplate.setNotificationType(notificationType);
NotificationTemplateConfig config = new NotificationTemplateConfig();
config.setDeliveryMethodsTemplates(new HashMap<>());
@@ -418,9 +444,9 @@ public class JavaRestClientTest extends AbstractContainerTest {
public void testApiKeyOperations() {
// Create an API key
ApiKeyInfo apiKeyInfo = new ApiKeyInfo();
- apiKeyInfo.setDescription("Test API Key " + RandomStringUtils.randomAlphabetic(5));
+ apiKeyInfo.setDescription("Test API Key " + RandomStringUtils.insecure().randomAlphabetic(5));
apiKeyInfo.setEnabled(true);
- apiKeyInfo.setUserId(user.getId());
+ apiKeyInfo.setUserId(tenantAdmin1.getId());
apiKeyInfo.setExpirationTime(0);
ApiKey savedApiKey = restClient.saveApiKey(apiKeyInfo);
@@ -428,18 +454,18 @@ public class JavaRestClientTest extends AbstractContainerTest {
assertThat(savedApiKey.getId()).isNotNull();
assertThat(savedApiKey.getDescription()).isEqualTo(apiKeyInfo.getDescription());
assertThat(savedApiKey.isEnabled()).isTrue();
- assertThat(savedApiKey.getUserId()).isEqualTo(user.getId());
- assertThat(savedApiKey.getTenantId()).isEqualTo(tenant.getId());
+ assertThat(savedApiKey.getUserId()).isEqualTo(tenantAdmin1.getId());
+ assertThat(savedApiKey.getTenantId()).isEqualTo(tenant1.getId());
assertThat(savedApiKey.getValue()).isNotNull();
// Get user API keys
- PageData apiKeys = restClient.getUserApiKeys(user.getId(), new PageLink(10));
+ PageData apiKeys = restClient.getUserApiKeys(tenantAdmin1.getId(), new PageLink(10));
assertThat(apiKeys).isNotNull();
assertThat(apiKeys.getData()).hasSize(1);
assertThat(apiKeys.getData().get(0).getId()).isEqualTo(savedApiKey.getId());
// Update API key description
- String updatedDescription = "Updated description " + RandomStringUtils.randomAlphabetic(5);
+ String updatedDescription = "Updated description " + RandomStringUtils.insecure().randomAlphabetic(5);
ApiKeyInfo updatedApiKeyInfo = restClient.updateApiKeyDescription(savedApiKey.getId(), updatedDescription);
assertThat(updatedApiKeyInfo).isNotNull();
assertThat(updatedApiKeyInfo.getDescription()).isEqualTo(updatedDescription);
@@ -458,8 +484,96 @@ public class JavaRestClientTest extends AbstractContainerTest {
restClient.deleteApiKey(savedApiKey.getId());
// Verify the API key is deleted
- PageData apiKeysAfterDelete = restClient.getUserApiKeys(user.getId(), new PageLink(10));
+ PageData apiKeysAfterDelete = restClient.getUserApiKeys(tenantAdmin1.getId(), new PageLink(10));
assertThat(apiKeysAfterDelete.getData()).isEmpty();
}
+ @Test
+ public void testGetDeviceProfileInfosByIds() {
+ var profileData = new DeviceProfileData();
+ profileData.setConfiguration(new DefaultDeviceProfileConfiguration());
+ profileData.setTransportConfiguration(new DefaultDeviceProfileTransportConfiguration());
+
+ // Create a device profile in tenant1 (current tenant)
+ var deviceProfile1 = new DeviceProfile();
+ deviceProfile1.setTenantId(tenant1.getId());
+ deviceProfile1.setName("Device Profile 1");
+ deviceProfile1.setType(DeviceProfileType.DEFAULT);
+ deviceProfile1.setTransportType(DeviceTransportType.DEFAULT);
+ deviceProfile1.setProfileData(profileData);
+ deviceProfile1 = restClient.saveDeviceProfile(deviceProfile1);
+
+ var deviceProfile2 = new DeviceProfile();
+ deviceProfile2.setTenantId(tenant1.getId());
+ deviceProfile2.setName("Device Profile 2");
+ deviceProfile2.setType(DeviceProfileType.DEFAULT);
+ deviceProfile2.setTransportType(DeviceTransportType.DEFAULT);
+ deviceProfile2.setProfileData(profileData);
+ deviceProfile2 = restClient.saveDeviceProfile(deviceProfile2);
+
+ // Create two more device profiles in tenant2 (different tenant)
+ restClient.login(tenantAdmin2.getEmail(), "password123");
+
+ var deviceProfile3 = new DeviceProfile();
+ deviceProfile3.setTenantId(tenant2.getId());
+ deviceProfile3.setName("Device Profile 3");
+ deviceProfile3.setType(DeviceProfileType.DEFAULT);
+ deviceProfile3.setTransportType(DeviceTransportType.DEFAULT);
+ deviceProfile3.setProfileData(profileData);
+ deviceProfile3 = restClient.saveDeviceProfile(deviceProfile3);
+
+ var deviceProfile4 = new DeviceProfile();
+ deviceProfile4.setTenantId(tenant2.getId());
+ deviceProfile4.setName("Device Profile 4");
+ deviceProfile4.setType(DeviceProfileType.DEFAULT);
+ deviceProfile4.setTransportType(DeviceTransportType.DEFAULT);
+ deviceProfile4.setProfileData(profileData);
+ deviceProfile4 = restClient.saveDeviceProfile(deviceProfile4);
+
+ // Attempt to fetch profiles 1 and 3 while acting as Tenant 2.
+ // - Profile 1: Filtered out because it belongs to a different tenant.
+ // - Profile 2: Filtered out because it belongs to a different tenant and was not requested.
+ // - Profile 3: Should be returned.
+ // - Profile 4: Filtered out; it belongs to the correct tenant, but was not requested by ID.
+ List profiles = restClient.getDeviceProfileInfosByIds(Set.of(deviceProfile1.getUuidId(), deviceProfile3.getUuidId()));
+ assertThat(profiles).hasSize(1);
+ assertThat(profiles.get(0).getId()).isEqualTo(deviceProfile3.getId());
+ }
+
+ @Test
+ public void testGetAssetProfilesByIds() {
+ // Create two asset profiles in tenant1 (current tenant)
+ var assetProfile1 = new AssetProfile();
+ assetProfile1.setTenantId(tenant1.getId());
+ assetProfile1.setName("Asset Profile 1");
+ assetProfile1 = restClient.saveAssetProfile(assetProfile1);
+
+ var assetProfile2 = new AssetProfile();
+ assetProfile2.setTenantId(tenant1.getId());
+ assetProfile2.setName("Asset Profile 2");
+ assetProfile2 = restClient.saveAssetProfile(assetProfile2);
+
+ // Create two more asset profiles in tenant2 (different tenant)
+ restClient.login(tenantAdmin2.getEmail(), "password123");
+
+ var assetProfile3 = new AssetProfile();
+ assetProfile3.setTenantId(tenant2.getId());
+ assetProfile3.setName("Asset Profile 3");
+ assetProfile3 = restClient.saveAssetProfile(assetProfile3);
+
+ var assetProfile4 = new AssetProfile();
+ assetProfile4.setTenantId(tenant2.getId());
+ assetProfile4.setName("Asset Profile 4");
+ assetProfile4 = restClient.saveAssetProfile(assetProfile4);
+
+ // Attempt to fetch profiles 1 and 3 while acting as Tenant 2.
+ // - Profile 1: Filtered out because it belongs to a different tenant.
+ // - Profile 2: Filtered out because it belongs to a different tenant and was not requested.
+ // - Profile 3: Should be returned.
+ // - Profile 4: Filtered out; it belongs to the correct tenant, but was not requested by ID.
+ List profiles = restClient.getAssetProfilesByIds(Set.of(assetProfile1.getUuidId(), assetProfile3.getUuidId()));
+ assertThat(profiles).hasSize(1);
+ assertThat(profiles.get(0).getId()).isEqualTo(assetProfile3.getId());
+ }
+
}
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/lwm2m/AbstractLwm2mClientTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/lwm2m/AbstractLwm2mClientTest.java
index f991e1b29c..c5673ae667 100644
--- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/lwm2m/AbstractLwm2mClientTest.java
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/lwm2m/AbstractLwm2mClientTest.java
@@ -57,12 +57,12 @@ import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.common.data.security.DeviceCredentialsType;
import org.thingsboard.server.msa.AbstractContainerTest;
+import org.thingsboard.server.msa.PortFinder;
import org.thingsboard.server.msa.WsClient;
import org.thingsboard.server.msa.connectivity.lwm2m.client.LwM2MTestClient;
import org.thingsboard.server.msa.connectivity.lwm2m.client.Lwm2mTestHelper.LwM2MClientState;
import org.thingsboard.server.msa.mapper.WsTelemetryResponse;
-import java.net.ServerSocket;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.util.ArrayList;
@@ -229,10 +229,8 @@ public class AbstractLwm2mClientTest extends AbstractContainerTest {
String endpoint, ScheduledExecutorService executor) throws Exception {
this.executor = executor;
LwM2MTestClient lwM2MTestClient = new LwM2MTestClient(executor, endpoint);
- try (ServerSocket socket = new ServerSocket(0)) {
- int clientPort = socket.getLocalPort();
- lwM2MTestClient.init(security, clientPort);
- }
+ int clientPort = PortFinder.findAvailableUdpPort();
+ lwM2MTestClient.init(security, clientPort);
return lwM2MTestClient;
}
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/lwm2m/client/LwM2MTestClient.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/lwm2m/client/LwM2MTestClient.java
index 64f4d6f08a..61a763562c 100644
--- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/lwm2m/client/LwM2MTestClient.java
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/lwm2m/client/LwM2MTestClient.java
@@ -65,6 +65,7 @@ import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import static org.assertj.core.api.Assertions.assertThat;
+import static org.awaitility.Awaitility.await;
import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_CONNECTION_ID_LENGTH;
import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_RECOMMENDED_CIPHER_SUITES_ONLY;
import static org.eclipse.leshan.core.LwM2mId.ACCESS_CONTROL;
@@ -72,6 +73,7 @@ import static org.eclipse.leshan.core.LwM2mId.DEVICE;
import static org.eclipse.leshan.core.LwM2mId.FIRMWARE;
import static org.eclipse.leshan.core.LwM2mId.SECURITY;
import static org.eclipse.leshan.core.LwM2mId.SERVER;
+import static org.thingsboard.server.msa.PortFinder.isUDPPortAvailable;
import static org.thingsboard.server.msa.connectivity.lwm2m.client.Lwm2mTestHelper.BINARY_APP_DATA_CONTAINER;
import static org.thingsboard.server.msa.connectivity.lwm2m.client.Lwm2mTestHelper.LwM2MClientState.ON_BOOTSTRAP_FAILURE;
import static org.thingsboard.server.msa.connectivity.lwm2m.client.Lwm2mTestHelper.LwM2MClientState.ON_BOOTSTRAP_STARTED;
@@ -95,7 +97,6 @@ import static org.thingsboard.server.msa.connectivity.lwm2m.client.Lwm2mTestHelp
import static org.thingsboard.server.msa.connectivity.lwm2m.client.Lwm2mTestHelper.serverId;
import static org.thingsboard.server.msa.connectivity.lwm2m.client.Lwm2mTestHelper.shortServerId;
-
@Slf4j
@Data
public class LwM2MTestClient {
@@ -115,8 +116,11 @@ public class LwM2MTestClient {
private int countUpdateRegistrationSuccess;
private int countReadObserveAfterUpdateRegistrationSuccess;
+ private int clientPort;
+
public void init(Security security, int clientPort) throws InvalidDDFFileException, IOException {
assertThat(leshanClient).as("client already initialized").isNull();
+ this.clientPort = clientPort;
List models = new ArrayList<>();
for (String resourceName : resources) {
@@ -342,12 +346,27 @@ public class LwM2MTestClient {
}
});
+ }
+
+ public void start() {
leshanClient.start();
+ if (clientPort > 0) {
+ log.error("Await UDP clientPort {} to be available before leshanClient.start()", clientPort);
+ await("Await UDP clientPort " + clientPort + " to be available before leshanClient.start()")
+ .atMost(30, TimeUnit.SECONDS)
+ .until(() -> isUDPPortAvailable(clientPort));
+ }
}
public void destroy() {
if (leshanClient != null) {
leshanClient.destroy(true);
+ if (clientPort > 0) {
+ log.error("Await UDP clientPort {} to disconnect after leshanClient.stop(deregister)", clientPort);
+ await("Await client UDP port " + clientPort + " to disconnect after leshanClient.stop(deregister)")
+ .atMost(30, TimeUnit.SECONDS)
+ .until(() -> isUDPPortAvailable(clientPort));
+ }
}
if (lwM2MDevice != null) {
lwM2MDevice.destroy();
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/rule/node/MqttNodeTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/rule/node/MqttNodeTest.java
index 67f6258767..6fff0b4b6a 100644
--- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/rule/node/MqttNodeTest.java
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/rule/node/MqttNodeTest.java
@@ -65,6 +65,7 @@ import static org.thingsboard.server.msa.prototypes.DevicePrototypes.defaultDevi
public class MqttNodeTest extends AbstractContainerTest {
private static final String TOPIC = "tb/mqtt/device";
+ private static final String CONTAINER_MQTT_URL = "tcp://localhost:1883";
private Device device;
@@ -94,7 +95,7 @@ public class MqttNodeTest extends AbstractContainerTest {
responseClient.connect();
responseClient.subscribe(TOPIC, messageListener);
- MqttClient mqttClient = new MqttClient("tcp://localhost:1883", StringUtils.randomAlphanumeric(10), new MemoryPersistence());
+ MqttClient mqttClient = new MqttClient(CONTAINER_MQTT_URL, StringUtils.randomAlphanumeric(10), new MemoryPersistence());
MqttConnectOptions mqttConnectOptions = new MqttConnectOptions();
mqttConnectOptions.setUserName(deviceCredentials.getCredentialsId());
mqttClient.connect(mqttConnectOptions);
diff --git a/msa/edqs/pom.xml b/msa/edqs/pom.xml
index 8de67e23b3..e6adae1f87 100644
--- a/msa/edqs/pom.xml
+++ b/msa/edqs/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 4.3.0-RC
+ 4.4.0-SNAPSHOT
msa
org.thingsboard.msa
diff --git a/msa/js-executor/package.json b/msa/js-executor/package.json
index df1d327c1b..cf7310a084 100644
--- a/msa/js-executor/package.json
+++ b/msa/js-executor/package.json
@@ -1,7 +1,7 @@
{
"name": "thingsboard-js-executor",
"private": true,
- "version": "4.3.0",
+ "version": "4.4.0",
"description": "ThingsBoard JavaScript Executor Microservice",
"main": "server.ts",
"bin": "server.js",
diff --git a/msa/js-executor/pom.xml b/msa/js-executor/pom.xml
index 6bd15adc43..0e75d24919 100644
--- a/msa/js-executor/pom.xml
+++ b/msa/js-executor/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 4.3.0-RC
+ 4.4.0-SNAPSHOT
msa
org.thingsboard.msa
diff --git a/msa/monitoring/pom.xml b/msa/monitoring/pom.xml
index fb10c11ec0..27b05f525d 100644
--- a/msa/monitoring/pom.xml
+++ b/msa/monitoring/pom.xml
@@ -22,7 +22,7 @@
4.0.0
org.thingsboard
- 4.3.0-RC
+ 4.4.0-SNAPSHOT
msa
diff --git a/msa/pom.xml b/msa/pom.xml
index b5c40015f7..04b1814063 100644
--- a/msa/pom.xml
+++ b/msa/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 4.3.0-RC
+ 4.4.0-SNAPSHOT
thingsboard
msa
@@ -133,7 +133,7 @@
- 4.3.0-latest
+ 4.4.0-latest
false
diff --git a/msa/tb-node/pom.xml b/msa/tb-node/pom.xml
index 078f2e1756..2244241347 100644
--- a/msa/tb-node/pom.xml
+++ b/msa/tb-node/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 4.3.0-RC
+ 4.4.0-SNAPSHOT
msa
org.thingsboard.msa
diff --git a/msa/tb/pom.xml b/msa/tb/pom.xml
index 8825ea7e33..1bf32786e7 100644
--- a/msa/tb/pom.xml
+++ b/msa/tb/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 4.3.0-RC
+ 4.4.0-SNAPSHOT
msa
org.thingsboard.msa
diff --git a/msa/transport/coap/pom.xml b/msa/transport/coap/pom.xml
index 9222a95ec2..e89a10e5a1 100644
--- a/msa/transport/coap/pom.xml
+++ b/msa/transport/coap/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard.msa
- 4.3.0-RC
+ 4.4.0-SNAPSHOT
transport
org.thingsboard.msa.transport
diff --git a/msa/transport/http/pom.xml b/msa/transport/http/pom.xml
index 4ecf529b2c..76b7ff9602 100644
--- a/msa/transport/http/pom.xml
+++ b/msa/transport/http/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard.msa
- 4.3.0-RC
+ 4.4.0-SNAPSHOT
transport
org.thingsboard.msa.transport
diff --git a/msa/transport/lwm2m/pom.xml b/msa/transport/lwm2m/pom.xml
index 9aec571309..9c10edfbb7 100644
--- a/msa/transport/lwm2m/pom.xml
+++ b/msa/transport/lwm2m/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard.msa
- 4.3.0-RC
+ 4.4.0-SNAPSHOT
transport
org.thingsboard.msa.transport
diff --git a/msa/transport/mqtt/pom.xml b/msa/transport/mqtt/pom.xml
index 2ad9d62c9d..ff759a52ff 100644
--- a/msa/transport/mqtt/pom.xml
+++ b/msa/transport/mqtt/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard.msa
- 4.3.0-RC
+ 4.4.0-SNAPSHOT
transport
org.thingsboard.msa.transport
diff --git a/msa/transport/pom.xml b/msa/transport/pom.xml
index f26c27cb4b..92978fcaa7 100644
--- a/msa/transport/pom.xml
+++ b/msa/transport/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 4.3.0-RC
+ 4.4.0-SNAPSHOT
msa
org.thingsboard.msa
diff --git a/msa/transport/snmp/pom.xml b/msa/transport/snmp/pom.xml
index a6bb9ebc8f..049cbad4ec 100644
--- a/msa/transport/snmp/pom.xml
+++ b/msa/transport/snmp/pom.xml
@@ -21,7 +21,7 @@
org.thingsboard.msa
transport
- 4.3.0-RC
+ 4.4.0-SNAPSHOT
org.thingsboard.msa.transport
diff --git a/msa/vc-executor-docker/pom.xml b/msa/vc-executor-docker/pom.xml
index a740f99743..183a82a170 100644
--- a/msa/vc-executor-docker/pom.xml
+++ b/msa/vc-executor-docker/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 4.3.0-RC
+ 4.4.0-SNAPSHOT
msa
org.thingsboard.msa
diff --git a/msa/vc-executor/pom.xml b/msa/vc-executor/pom.xml
index cb71d86ef4..6b0277c2c6 100644
--- a/msa/vc-executor/pom.xml
+++ b/msa/vc-executor/pom.xml
@@ -21,7 +21,7 @@
org.thingsboard
- 4.3.0-RC
+ 4.4.0-SNAPSHOT
msa
org.thingsboard.msa
diff --git a/msa/web-ui/package.json b/msa/web-ui/package.json
index 0c22a38465..4544aece37 100644
--- a/msa/web-ui/package.json
+++ b/msa/web-ui/package.json
@@ -1,7 +1,7 @@
{
"name": "thingsboard-web-ui",
"private": true,
- "version": "4.3.0",
+ "version": "4.4.0",
"description": "ThingsBoard Web UI Microservice",
"main": "server.ts",
"bin": "server.js",
diff --git a/msa/web-ui/pom.xml b/msa/web-ui/pom.xml
index 40059304ba..7dfc4c0aee 100644
--- a/msa/web-ui/pom.xml
+++ b/msa/web-ui/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 4.3.0-RC
+ 4.4.0-SNAPSHOT
msa
org.thingsboard.msa
diff --git a/netty-mqtt/pom.xml b/netty-mqtt/pom.xml
index 4ea598d363..6856da9233 100644
--- a/netty-mqtt/pom.xml
+++ b/netty-mqtt/pom.xml
@@ -19,11 +19,11 @@
4.0.0
org.thingsboard
- 4.3.0-RC
+ 4.4.0-SNAPSHOT
thingsboard
netty-mqtt
- 4.3.0-RC
+ 4.4.0-SNAPSHOT
jar
Netty MQTT Client
diff --git a/pom.xml b/pom.xml
index 92a002cde0..c4cfec0ad3 100755
--- a/pom.xml
+++ b/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
thingsboard
- 4.3.0-RC
+ 4.4.0-SNAPSHOT
pom
Thingsboard
@@ -69,7 +69,7 @@
1.2.5
1.2.5
1.7.1
- 3.2.5
+ 3.5.4
3.4.0
2.8.8TB
2.2.30
@@ -127,7 +127,7 @@
5.0.0
7.10.1
- 1.20.6
+ 1.21.4
1.0.2
1.12
6.1.0
@@ -662,8 +662,10 @@
${surefire.version}
- -XX:+UseStringDeduplication -XX:MaxGCPauseMillis=20
- --add-opens java.base/java.lang.reflect=ALL-UNNAMED
+ -XX:+UseStringDeduplication -XX:MaxGCPauseMillis=200
+ --add-opens=java.base/java.lang.reflect=ALL-UNNAMED
+ -Dqueue.edqs.local.rocksdb_path="target/rocksdb/fork_${surefire.forkNumber}/edqs"
+ -Dqueue.calculated_fields.rocks_db_path="target/rocksdb/fork_${surefire.forkNumber}/cf"
diff --git a/rest-client/pom.xml b/rest-client/pom.xml
index 1b33aca512..9996b19d00 100644
--- a/rest-client/pom.xml
+++ b/rest-client/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 4.3.0-RC
+ 4.4.0-SNAPSHOT
thingsboard
rest-client
@@ -51,6 +51,10 @@
com.auth0
java-jwt
+
+ org.apache.httpcomponents.core5
+ httpcore5
+
diff --git a/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java b/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java
index bd1d7b05c3..6c97a23a90 100644
--- a/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java
+++ b/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java
@@ -23,6 +23,7 @@ import lombok.Getter;
import lombok.SneakyThrows;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.concurrent.LazyInitializer;
+import org.apache.hc.core5.net.URIBuilder;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.Resource;
@@ -157,11 +158,11 @@ import org.thingsboard.server.common.data.oauth2.PlatformType;
import org.thingsboard.server.common.data.ota.ChecksumAlgorithm;
import org.thingsboard.server.common.data.ota.OtaPackageType;
import org.thingsboard.server.common.data.page.PageData;
-import org.thingsboard.server.common.data.pat.ApiKey;
-import org.thingsboard.server.common.data.pat.ApiKeyInfo;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.page.SortOrder;
import org.thingsboard.server.common.data.page.TimePageLink;
+import org.thingsboard.server.common.data.pat.ApiKey;
+import org.thingsboard.server.common.data.pat.ApiKeyInfo;
import org.thingsboard.server.common.data.plugin.ComponentDescriptor;
import org.thingsboard.server.common.data.plugin.ComponentType;
import org.thingsboard.server.common.data.query.AlarmCountQuery;
@@ -210,17 +211,21 @@ import org.thingsboard.server.common.data.widget.WidgetsBundle;
import java.io.Closeable;
import java.io.IOException;
import java.net.URI;
+import java.net.URISyntaxException;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Optional;
+import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
+import static java.util.stream.Collectors.joining;
import static org.thingsboard.server.common.data.StringUtils.isEmpty;
public class RestClient implements Closeable {
@@ -1588,6 +1593,33 @@ public class RestClient implements Closeable {
}, activeOnly).getBody();
}
+ public List getDeviceProfileInfosByIds(Set ids) {
+ URIBuilder builder;
+ try {
+ builder = new URIBuilder(baseURL);
+ } catch (URISyntaxException e) {
+ throw new IllegalArgumentException("Invalid base URL: " + baseURL, e);
+ }
+
+ builder.appendPath("/api/deviceProfileInfos");
+
+ String commaSeparatedIds = ids.stream()
+ .filter(Objects::nonNull)
+ .map(UUID::toString)
+ .collect(joining(","));
+
+ builder.addParameter("deviceProfileIds", commaSeparatedIds);
+
+ URI uri;
+ try {
+ uri = builder.build();
+ } catch (URISyntaxException e) {
+ throw new IllegalStateException("Failed to construct API URI from base URL and provided params", e);
+ }
+
+ return restTemplate.exchange(uri, HttpMethod.GET, null, new ParameterizedTypeReference>() {}).getBody();
+ }
+
public JsonNode claimDevice(String deviceName, ClaimRequest claimRequest) {
return restTemplate.exchange(
baseURL + "/api/customer/device/{deviceName}/claim",
@@ -1824,6 +1856,33 @@ public class RestClient implements Closeable {
}, params).getBody();
}
+ public List getAssetProfilesByIds(Set ids) {
+ URIBuilder builder;
+ try {
+ builder = new URIBuilder(baseURL);
+ } catch (URISyntaxException e) {
+ throw new IllegalArgumentException("Invalid base URL: " + baseURL, e);
+ }
+
+ builder.appendPath("/api/assetProfileInfos");
+
+ String commaSeparatedIds = ids.stream()
+ .filter(Objects::nonNull)
+ .map(UUID::toString)
+ .collect(joining(","));
+
+ builder.addParameter("assetProfileIds", commaSeparatedIds);
+
+ URI uri;
+ try {
+ uri = builder.build();
+ } catch (URISyntaxException e) {
+ throw new IllegalStateException("Failed to construct API URI from base URL and provided params", e);
+ }
+
+ return restTemplate.exchange(uri, HttpMethod.GET, null, new ParameterizedTypeReference>() {}).getBody();
+ }
+
public Long countEntitiesByQuery(EntityCountQuery query) {
return restTemplate.postForObject(baseURL + "/api/entitiesQuery/count", query, Long.class);
}
diff --git a/rule-engine/pom.xml b/rule-engine/pom.xml
index feb8cbf8f1..bba58e92a8 100644
--- a/rule-engine/pom.xml
+++ b/rule-engine/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 4.3.0-RC
+ 4.4.0-SNAPSHOT
thingsboard
rule-engine
diff --git a/rule-engine/rule-engine-api/pom.xml b/rule-engine/rule-engine-api/pom.xml
index 7523afc0b6..a9f58a08b3 100644
--- a/rule-engine/rule-engine-api/pom.xml
+++ b/rule-engine/rule-engine-api/pom.xml
@@ -22,7 +22,7 @@
4.0.0
org.thingsboard
- 4.3.0-RC
+ 4.4.0-SNAPSHOT
rule-engine
org.thingsboard.rule-engine
diff --git a/rule-engine/rule-engine-components/pom.xml b/rule-engine/rule-engine-components/pom.xml
index f9218b69de..4e092211b8 100644
--- a/rule-engine/rule-engine-components/pom.xml
+++ b/rule-engine/rule-engine-components/pom.xml
@@ -22,7 +22,7 @@
4.0.0
org.thingsboard
- 4.3.0-RC
+ 4.4.0-SNAPSHOT
rule-engine
org.thingsboard.rule-engine
diff --git a/tools/pom.xml b/tools/pom.xml
index 20c8fc95fc..ed02dbf836 100644
--- a/tools/pom.xml
+++ b/tools/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 4.3.0-RC
+ 4.4.0-SNAPSHOT
thingsboard
tools
diff --git a/transport/coap/pom.xml b/transport/coap/pom.xml
index ded25e8260..c012e863f9 100644
--- a/transport/coap/pom.xml
+++ b/transport/coap/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 4.3.0-RC
+ 4.4.0-SNAPSHOT
transport
org.thingsboard.transport
diff --git a/transport/http/pom.xml b/transport/http/pom.xml
index 2c83bebd0a..87f3ccec21 100644
--- a/transport/http/pom.xml
+++ b/transport/http/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 4.3.0-RC
+ 4.4.0-SNAPSHOT
transport
org.thingsboard.transport
diff --git a/transport/lwm2m/pom.xml b/transport/lwm2m/pom.xml
index e28fd452cf..d92d41388e 100644
--- a/transport/lwm2m/pom.xml
+++ b/transport/lwm2m/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 4.3.0-RC
+ 4.4.0-SNAPSHOT
transport
org.thingsboard.transport
diff --git a/transport/mqtt/pom.xml b/transport/mqtt/pom.xml
index 2bee6f94e4..f99409c470 100644
--- a/transport/mqtt/pom.xml
+++ b/transport/mqtt/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 4.3.0-RC
+ 4.4.0-SNAPSHOT
transport
org.thingsboard.transport
diff --git a/transport/pom.xml b/transport/pom.xml
index 89c0355407..3b6195f9d8 100644
--- a/transport/pom.xml
+++ b/transport/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 4.3.0-RC
+ 4.4.0-SNAPSHOT
thingsboard
transport
diff --git a/transport/snmp/pom.xml b/transport/snmp/pom.xml
index 38682ade69..54a949f320 100644
--- a/transport/snmp/pom.xml
+++ b/transport/snmp/pom.xml
@@ -21,7 +21,7 @@
org.thingsboard
- 4.3.0-RC
+ 4.4.0-SNAPSHOT
transport
diff --git a/ui-ngx/package.json b/ui-ngx/package.json
index 3403e7a3aa..c1385e46d2 100644
--- a/ui-ngx/package.json
+++ b/ui-ngx/package.json
@@ -1,6 +1,6 @@
{
"name": "thingsboard",
- "version": "4.3.0",
+ "version": "4.4.0",
"scripts": {
"ng": "ng",
"start": "node --max_old_space_size=8048 ./node_modules/@angular/cli/bin/ng serve --configuration development --host 0.0.0.0 --open",
diff --git a/ui-ngx/pom.xml b/ui-ngx/pom.xml
index bc0f5a4ed8..11ce2dcf5f 100644
--- a/ui-ngx/pom.xml
+++ b/ui-ngx/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 4.3.0-RC
+ 4.4.0-SNAPSHOT
thingsboard
org.thingsboard