diff --git a/common/cluster-api/src/main/proto/data.proto b/common/cluster-api/src/main/proto/data.proto
new file mode 100644
index 0000000000..7e12465704
--- /dev/null
+++ b/common/cluster-api/src/main/proto/data.proto
@@ -0,0 +1,67 @@
+/**
+ * Copyright © 2016-2023 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.
+ */
+syntax = "proto3";
+
+package transport;
+
+option java_package = "org.thingsboard.server.gen.data";
+option java_multiple_files = true;
+
+/**
+ * Data Serialization Structures;
+ */
+message DeviceProto {
+ string id = 1;
+ int64 createdTime = 3;
+ bytes additionalInfo = 4;
+ string tenantId = 5;
+ optional string customerId = 6;
+ optional string name = 7;
+ optional string type = 8;
+ optional string label = 9;
+ optional string deviceProfileId = 10;
+ bytes deviceDataBytes = 11;
+ optional string firmwareId = 12;
+ optional string softwareId = 13;
+ optional string externalId = 14;
+}
+
+message TbUUID {
+
+}
+
+message DeviceProto2 {
+ int64 idM = 1;
+ int64 idL = 2;
+ int64 createdTime = 3;
+ bytes additionalInfo = 4;
+ int64 tenantIdM = 5;
+ int64 tenantIdL = 6;
+ optional int64 customerIdM = 7;
+ optional int64 customerIdL = 8;
+ optional string name = 9;
+ optional string type = 10;
+ optional string label = 11;
+ optional int64 deviceProfileIdM = 12;
+ optional int64 deviceProfileIdL = 13;
+ bytes deviceDataBytes = 14;
+ optional int64 firmwareIdM = 15;
+ optional int64 firmwareIdL = 16;
+ optional int64 softwareIdM = 17;
+ optional int64 softwareIdL = 18;
+ optional int64 externalIdM = 19;
+ optional int64 externalIdL = 20;
+}
\ No newline at end of file
diff --git a/common/queue/pom.xml b/common/queue/pom.xml
index a995d9b4a3..c913db54ab 100644
--- a/common/queue/pom.xml
+++ b/common/queue/pom.xml
@@ -120,6 +120,10 @@
org.apache.curator
curator-recipes
+
+ org.mapstruct
+ mapstruct
+
org.springframework.boot
spring-boot-starter-test
diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/util/DeviceMapper.java b/common/queue/src/main/java/org/thingsboard/server/queue/util/DeviceMapper.java
new file mode 100644
index 0000000000..8fa630658a
--- /dev/null
+++ b/common/queue/src/main/java/org/thingsboard/server/queue/util/DeviceMapper.java
@@ -0,0 +1,34 @@
+package org.thingsboard.server.queue.util;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.google.protobuf.ByteString;
+import org.mapstruct.CollectionMappingStrategy;
+import org.mapstruct.Mapper;
+import org.mapstruct.NullValueCheckStrategy;
+import org.mapstruct.factory.Mappers;
+import org.thingsboard.common.util.JacksonUtil;
+import org.thingsboard.server.common.data.Device;
+import org.thingsboard.server.common.data.id.UUIDBased;
+import org.thingsboard.server.gen.data.DeviceProto;
+
+import java.nio.charset.StandardCharsets;
+
+@Mapper(collectionMappingStrategy = CollectionMappingStrategy.ADDER_PREFERRED,
+ nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS)
+public interface DeviceMapper {
+
+ DeviceMapper INSTANCE = Mappers.getMapper(DeviceMapper.class);
+
+ DeviceProto map(Device device);
+
+ default String map(UUIDBased id) {
+ return id.getId().toString();
+ }
+
+ default ByteString map(JsonNode additionalInfo) {
+ return ByteString.copyFrom(JacksonUtil.toString(additionalInfo), StandardCharsets.UTF_8);
+ }
+
+// Device map(DeviceProto proto);
+
+}
diff --git a/common/queue/src/test/java/org/thingsboard/server/queue/util/DataDecodingEncodingServiceTest.java b/common/queue/src/test/java/org/thingsboard/server/queue/util/DataDecodingEncodingServiceTest.java
index e7d0adf909..edb22c35f8 100644
--- a/common/queue/src/test/java/org/thingsboard/server/queue/util/DataDecodingEncodingServiceTest.java
+++ b/common/queue/src/test/java/org/thingsboard/server/queue/util/DataDecodingEncodingServiceTest.java
@@ -1,293 +1,186 @@
package org.thingsboard.server.queue.util;
+import lombok.SneakyThrows;
+import org.junit.Assert;
import org.junit.Ignore;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
-import org.springframework.util.SerializationUtils;
import org.thingsboard.common.util.JacksonUtil;
-import org.thingsboard.server.common.data.DeviceProfile;
+import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.FSTUtils;
+import org.thingsboard.server.common.data.StringUtils;
+import org.thingsboard.server.common.data.id.CustomerId;
+import org.thingsboard.server.common.data.id.DeviceId;
+import org.thingsboard.server.common.data.id.OtaPackageId;
+import org.thingsboard.server.common.data.id.TenantId;
+import org.thingsboard.server.common.data.id.UUIDBased;
+import org.thingsboard.server.gen.data.DeviceProto;
+import org.thingsboard.server.gen.data.DeviceProto2;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.nio.charset.StandardCharsets;
+import java.util.UUID;
+import java.util.function.Consumer;
import java.util.function.Function;
+import java.util.function.Supplier;
public class DataDecodingEncodingServiceTest {
private static final int _1M = 1000 * 1000;
- private static String DEVICE_PROFILE_STR = "{\n" +
- " \"name\": \"thermostat\",\n" +
- " \"description\": \"Thermostat device profile\",\n" +
- " \"image\": null,\n" +
- " \"type\": \"DEFAULT\",\n" +
- " \"transportType\": \"DEFAULT\",\n" +
- " \"provisionType\": \"DISABLED\",\n" +
- " \"defaultRuleChainId\": {\n" +
- " \"entityType\": \"RULE_CHAIN\",\n" +
- " \"id\": \"eaaa47b0-e827-11ed-9f13-7f5975adc62f\"\n" +
- " },\n" +
- " \"defaultDashboardId\": null,\n" +
- " \"defaultQueueName\": null,\n" +
- " \"profileData\": {\n" +
- " \"configuration\": {\n" +
- " \"type\": \"DEFAULT\"\n" +
- " },\n" +
- " \"transportConfiguration\": {\n" +
- " \"type\": \"DEFAULT\"\n" +
- " },\n" +
- " \"provisionConfiguration\": {\n" +
- " \"type\": \"DISABLED\",\n" +
- " \"provisionDeviceSecret\": null\n" +
- " },\n" +
- " \"alarms\": [\n" +
- " {\n" +
- " \"id\": \"highTemperatureAlarmID\",\n" +
- " \"alarmType\": \"High Temperature\",\n" +
- " \"createRules\": {\n" +
- " \"MAJOR\": {\n" +
- " \"condition\": {\n" +
- " \"condition\": [\n" +
- " {\n" +
- " \"key\": {\n" +
- " \"type\": \"ATTRIBUTE\",\n" +
- " \"key\": \"temperatureAlarmFlag\"\n" +
- " },\n" +
- " \"valueType\": \"BOOLEAN\",\n" +
- " \"value\": null,\n" +
- " \"predicate\": {\n" +
- " \"type\": \"BOOLEAN\",\n" +
- " \"operation\": \"EQUAL\",\n" +
- " \"value\": {\n" +
- " \"defaultValue\": true,\n" +
- " \"userValue\": null,\n" +
- " \"dynamicValue\": null\n" +
- " }\n" +
- " }\n" +
- " },\n" +
- " {\n" +
- " \"key\": {\n" +
- " \"type\": \"TIME_SERIES\",\n" +
- " \"key\": \"temperature\"\n" +
- " },\n" +
- " \"valueType\": \"NUMERIC\",\n" +
- " \"value\": null,\n" +
- " \"predicate\": {\n" +
- " \"type\": \"NUMERIC\",\n" +
- " \"operation\": \"GREATER\",\n" +
- " \"value\": {\n" +
- " \"defaultValue\": 25,\n" +
- " \"userValue\": null,\n" +
- " \"dynamicValue\": {\n" +
- " \"sourceType\": \"CURRENT_DEVICE\",\n" +
- " \"sourceAttribute\": \"temperatureAlarmThreshold\",\n" +
- " \"inherit\": false\n" +
- " }\n" +
- " }\n" +
- " }\n" +
- " }\n" +
- " ],\n" +
- " \"spec\": {\n" +
- " \"type\": \"SIMPLE\"\n" +
- " }\n" +
- " },\n" +
- " \"schedule\": null,\n" +
- " \"alarmDetails\": \"Current temperature = ${temperature}\",\n" +
- " \"dashboardId\": null\n" +
- " }\n" +
- " },\n" +
- " \"clearRule\": {\n" +
- " \"condition\": {\n" +
- " \"condition\": [\n" +
- " {\n" +
- " \"key\": {\n" +
- " \"type\": \"TIME_SERIES\",\n" +
- " \"key\": \"temperature\"\n" +
- " },\n" +
- " \"valueType\": \"NUMERIC\",\n" +
- " \"value\": null,\n" +
- " \"predicate\": {\n" +
- " \"type\": \"NUMERIC\",\n" +
- " \"operation\": \"LESS_OR_EQUAL\",\n" +
- " \"value\": {\n" +
- " \"defaultValue\": 25,\n" +
- " \"userValue\": null,\n" +
- " \"dynamicValue\": {\n" +
- " \"sourceType\": \"CURRENT_DEVICE\",\n" +
- " \"sourceAttribute\": \"temperatureAlarmThreshold\",\n" +
- " \"inherit\": false\n" +
- " }\n" +
- " }\n" +
- " }\n" +
- " }\n" +
- " ],\n" +
- " \"spec\": {\n" +
- " \"type\": \"SIMPLE\"\n" +
- " }\n" +
- " },\n" +
- " \"schedule\": null,\n" +
- " \"alarmDetails\": \"Current temperature = ${temperature}\",\n" +
- " \"dashboardId\": null\n" +
- " },\n" +
- " \"propagate\": false,\n" +
- " \"propagateToOwner\": false,\n" +
- " \"propagateToTenant\": false,\n" +
- " \"propagateRelationTypes\": null\n" +
- " },\n" +
- " {\n" +
- " \"id\": \"lowHumidityAlarmID\",\n" +
- " \"alarmType\": \"Low Humidity\",\n" +
- " \"createRules\": {\n" +
- " \"MINOR\": {\n" +
- " \"condition\": {\n" +
- " \"condition\": [\n" +
- " {\n" +
- " \"key\": {\n" +
- " \"type\": \"ATTRIBUTE\",\n" +
- " \"key\": \"humidityAlarmFlag\"\n" +
- " },\n" +
- " \"valueType\": \"BOOLEAN\",\n" +
- " \"value\": null,\n" +
- " \"predicate\": {\n" +
- " \"type\": \"BOOLEAN\",\n" +
- " \"operation\": \"EQUAL\",\n" +
- " \"value\": {\n" +
- " \"defaultValue\": true,\n" +
- " \"userValue\": null,\n" +
- " \"dynamicValue\": null\n" +
- " }\n" +
- " }\n" +
- " },\n" +
- " {\n" +
- " \"key\": {\n" +
- " \"type\": \"TIME_SERIES\",\n" +
- " \"key\": \"humidity\"\n" +
- " },\n" +
- " \"valueType\": \"NUMERIC\",\n" +
- " \"value\": null,\n" +
- " \"predicate\": {\n" +
- " \"type\": \"NUMERIC\",\n" +
- " \"operation\": \"LESS\",\n" +
- " \"value\": {\n" +
- " \"defaultValue\": 60,\n" +
- " \"userValue\": null,\n" +
- " \"dynamicValue\": {\n" +
- " \"sourceType\": \"CURRENT_DEVICE\",\n" +
- " \"sourceAttribute\": \"humidityAlarmThreshold\",\n" +
- " \"inherit\": false\n" +
- " }\n" +
- " }\n" +
- " }\n" +
- " }\n" +
- " ],\n" +
- " \"spec\": {\n" +
- " \"type\": \"SIMPLE\"\n" +
- " }\n" +
- " },\n" +
- " \"schedule\": null,\n" +
- " \"alarmDetails\": \"Current humidity = ${humidity}\",\n" +
- " \"dashboardId\": null\n" +
- " }\n" +
- " },\n" +
- " \"clearRule\": {\n" +
- " \"condition\": {\n" +
- " \"condition\": [\n" +
- " {\n" +
- " \"key\": {\n" +
- " \"type\": \"TIME_SERIES\",\n" +
- " \"key\": \"humidity\"\n" +
- " },\n" +
- " \"valueType\": \"NUMERIC\",\n" +
- " \"value\": null,\n" +
- " \"predicate\": {\n" +
- " \"type\": \"NUMERIC\",\n" +
- " \"operation\": \"GREATER_OR_EQUAL\",\n" +
- " \"value\": {\n" +
- " \"defaultValue\": 60,\n" +
- " \"userValue\": null,\n" +
- " \"dynamicValue\": {\n" +
- " \"sourceType\": \"CURRENT_DEVICE\",\n" +
- " \"sourceAttribute\": \"humidityAlarmThreshold\",\n" +
- " \"inherit\": false\n" +
- " }\n" +
- " }\n" +
- " }\n" +
- " }\n" +
- " ],\n" +
- " \"spec\": {\n" +
- " \"type\": \"SIMPLE\"\n" +
- " }\n" +
- " },\n" +
- " \"schedule\": null,\n" +
- " \"alarmDetails\": \"Current humidity = ${humidity}\",\n" +
- " \"dashboardId\": null\n" +
- " },\n" +
- " \"propagate\": false,\n" +
- " \"propagateToOwner\": false,\n" +
- " \"propagateToTenant\": false,\n" +
- " \"propagateRelationTypes\": null\n" +
- " }\n" +
- " ]\n" +
- " },\n" +
- " \"provisionDeviceKey\": null,\n" +
- " \"firmwareId\": null,\n" +
- " \"softwareId\": null,\n" +
- " \"defaultEdgeRuleChainId\": null,\n" +
- " \"externalId\": null,\n" +
- " \"default\": false\n" +
- "}";
- private static DeviceProfile dp;
+ private static Device d;
@BeforeAll
public static void before() {
- dp = JacksonUtil.fromString(DEVICE_PROFILE_STR, DeviceProfile.class);
+ d = new Device();
+ d.setId(new DeviceId(UUID.randomUUID()));
+ d.setCreatedTime(System.currentTimeMillis());
+ d.setTenantId(new TenantId(UUID.randomUUID()));
+ d.setCustomerId(new CustomerId(UUID.randomUUID()));
+ d.setName(StringUtils.randomAlphabetic(10));
+ d.setLabel(StringUtils.randomAlphabetic(10));
+ d.setType(StringUtils.randomAlphabetic(10));
+// d.setAdditionalInfo(JacksonUtil.newObjectNode().put("smth", StringUtils.randomAlphabetic(100)));
+ d.setFirmwareId(new OtaPackageId(UUID.randomUUID()));
+ d.setSoftwareId(new OtaPackageId(UUID.randomUUID()));
+ d.setExternalId(new DeviceId(UUID.randomUUID()));
}
@Ignore
@Test
- void testDecode() {
+ void testSerialize() {
+
+ Assert.assertArrayEquals(toProto(d), toMapStruct(d));
// warmup
- testDecode("FST", FSTUtils::encode);
- testDecode("Jackson", DataDecodingEncodingServiceTest::serializeJackson);
- testDecode("Default", SerializationUtils::serialize);
+ testSerialize("FST", FSTUtils::encode);
+ testSerialize("Proto", this::toProto);
+ testSerialize("Proto2", this::toProto2);
+ testSerialize("MapStruct", this::toMapStruct);
+ testSerialize("Jackson", DataDecodingEncodingServiceTest::serializeJackson);
+// testSerialize("Default", SerializationUtils::serialize);
+
+ testDeserialize("FST", FSTUtils.encode(d), FSTUtils::decode);
+ testDeserialize("Proto", toProto(d), this::fromProto);
+ testDeserialize("Jackson", serializeJackson(d), data -> JacksonUtil.fromBytes(data, Device.class));
+// testDeserialize("Default", serializeDefault(d), data -> (Device) SerializationUtils.deserialize(data));
+ }
+
+ private byte[] toMapStruct(Device device) {
+ return DeviceMapper.INSTANCE.map(device).toByteArray();
+ }
+
+ private byte[] toProto(Device device) {
+ var builder = DeviceProto.newBuilder();
+ builder.setId(device.getId().getId().toString());
+ builder.setCreatedTime(device.getCreatedTime());
+ builder.setTenantId(device.getTenantId().getId().toString());
+ toProto(device::getCustomerId, builder::setCustomerId);
+ builder.setName(device.getName());
+ builder.setLabel(device.getLabel());
+ builder.setType(device.getType());
+ toProto(device::getFirmwareId, builder::setFirmwareId);
+ toProto(device::getSoftwareId, builder::setSoftwareId);
+ toProto(device::getExternalId, builder::setExternalId);
+ return builder.build().toByteArray();
+ }
- testEncode("FST", FSTUtils.encode(dp), FSTUtils::decode);
- testEncode("Jackson", serializeJackson(dp), data -> JacksonUtil.fromBytes(data, DeviceProfile.class));
- testEncode("Default", serializeDefault(dp), data -> (DeviceProfile) SerializationUtils.deserialize(data));
+ private byte[] toProto2(Device device) {
+ var builder = DeviceProto2.newBuilder();
+ builder.setIdM(device.getId().getId().getMostSignificantBits());
+ builder.setIdL(device.getId().getId().getLeastSignificantBits());
+ builder.setCreatedTime(device.getCreatedTime());
+ builder.setTenantIdM(device.getTenantId().getId().getMostSignificantBits());
+ builder.setTenantIdL(device.getTenantId().getId().getLeastSignificantBits());
+ toProto(device::getCustomerId, builder::setCustomerIdM, builder::setCustomerIdL);
+ builder.setName(device.getName());
+ builder.setLabel(device.getLabel());
+ builder.setType(device.getType());
+ toProto(device::getFirmwareId, builder::setFirmwareIdM, builder::setFirmwareIdL);
+ toProto(device::getSoftwareId, builder::setSoftwareIdM, builder::setSoftwareIdL);
+ toProto(device::getExternalId, builder::setExternalIdM, builder::setExternalIdL);
+ return builder.build().toByteArray();
}
- private void testDecode(String methodName, Function serializeFunction) {
+ @SneakyThrows
+ private Device fromProto(byte[] bytes) {
+ DeviceProto dp = DeviceProto.parseFrom(bytes);
+ Device d = new Device();
+ d.setId(new DeviceId(UUID.fromString(dp.getId())));
+ d.setCreatedTime(dp.getCreatedTime());
+ d.setTenantId(new TenantId(UUID.fromString(dp.getTenantId())));
+ if (dp.hasCustomerId()) {
+ d.setCustomerId(new CustomerId(UUID.fromString(dp.getCustomerId())));
+ }
+ if (dp.hasName()) {
+ d.setName(dp.getName());
+ }
+ if (dp.hasLabel()) {
+ d.setLabel(dp.getLabel());
+ }
+ if (dp.hasType()) {
+ d.setType(dp.getType());
+ }
+ if (dp.hasFirmwareId()) {
+ d.setFirmwareId(new OtaPackageId(UUID.fromString(dp.getFirmwareId())));
+ }
+ if (dp.hasSoftwareId()) {
+ d.setSoftwareId(new OtaPackageId(UUID.fromString(dp.getSoftwareId())));
+ }
+ if (dp.hasExternalId()) {
+ d.setExternalId(new DeviceId(UUID.fromString(dp.getExternalId())));
+ }
+ return d;
+ }
+
+
+ private void toProto(Supplier extractId, Consumer setId) {
+ UUIDBased id = extractId.get();
+ if (id != null) {
+ setId.accept(id.getId().toString());
+ }
+ }
+
+ private void toProto(Supplier extractId, Consumer setIdM, Consumer setIdL) {
+ UUIDBased id = extractId.get();
+ if (id != null) {
+ var uuid = id.getId();
+ setIdM.accept(uuid.getMostSignificantBits());
+ setIdL.accept(uuid.getLeastSignificantBits());
+ }
+ }
+
+ private void testSerialize(String methodName, Function serializeFunction) {
// WARMUP;
- for (int i = 0; i < 1000; i++) {
- dp.setName(Integer.toString(i));
- serializeFunction.apply(dp);
+ for (int i = 0; i < _1M; i++) {
+ d.setName(Integer.toString(i));
+ serializeFunction.apply(d);
}
long startTs = System.nanoTime();
for (int i = 0; i < _1M; i++) {
- dp.setName(Integer.toString(i));
- serializeFunction.apply(dp);
+ d.setName(Integer.toString(i));
+ serializeFunction.apply(d);
}
long elapsedTime = System.nanoTime() - startTs;
- System.out.println("S: " + methodName + " SIZE: " + serializeFunction.apply(dp).length + " TIME: " + elapsedTime + "ns or " + (elapsedTime / 1000000) + "ms");
+ System.out.println("S: " + methodName + " SIZE: " + serializeFunction.apply(d).length + " TIME: " + (elapsedTime / 1000000) + "ms");
}
- private void testEncode(String methodName, byte[] data, Function deserializeFunction) {
+ private void testDeserialize(String methodName, byte[] data, Function deserializeFunction) {
// WARMUP;
for (int i = 0; i < 1000; i++) {
- dp.setName(Integer.toString(i));
+ d.setName(Integer.toString(i));
deserializeFunction.apply(data);
}
long startTs = System.nanoTime();
for (int i = 0; i < _1M; i++) {
- dp.setName(Integer.toString(i));
+ d.setName(Integer.toString(i));
deserializeFunction.apply(data);
}
long elapsedTime = System.nanoTime() - startTs;
- System.out.println("D: " +methodName + " TIME: " + elapsedTime + "ns or " + (elapsedTime / 1000000) + "ms");
+ System.out.println("D: " + methodName + " TIME: " + (elapsedTime / 1000000) + "ms");
}
static byte[] serializeDefault(T object) {
diff --git a/pom.xml b/pom.xml
index 98b2e1529a..cdbb4a60f3 100755
--- a/pom.xml
+++ b/pom.xml
@@ -80,6 +80,7 @@
1.42.1
1.0.6
1.18.18
+ 1.5.5.Final
1.2.4
1.2.5
4.1.91.Final
@@ -619,6 +620,11 @@
lombok
${lombok.version}
+
+ org.mapstruct
+ mapstruct-processor
+ ${mapstruct.version}
+
@@ -1700,6 +1706,11 @@
${dbunit.version}
test
+
+ org.mapstruct
+ mapstruct
+ ${mapstruct.version}
+
org.projectlombok
lombok