From 27c6ac6722f4f889aa4b247c74b637ab8c1fbde8 Mon Sep 17 00:00:00 2001 From: Taisiia Goltseva Date: Tue, 20 Oct 2020 18:42:15 +0400 Subject: [PATCH] Deserialize Protobuf messages using descriptor files --- README.md | 34 + application.example.yml | 13 + build.gradle | 4 + .../java/org/akhq/configs/Connection.java | 11 +- .../java/org/akhq/configs/TopicsMapping.java | 15 + src/main/java/org/akhq/models/Record.java | 20 +- .../java/org/akhq/modules/KafkaModule.java | 2 +- .../CustomDeserializerRepository.java | 26 + .../akhq/repositories/RecordRepository.java | 10 +- .../utils/ProtobufToJsonDeserializer.java | 142 +++ src/test/java/org/akhq/utils/Album.java | 15 + src/test/java/org/akhq/utils/AlbumProto.java | 1120 +++++++++++++++++ src/test/java/org/akhq/utils/Film.java | 16 + src/test/java/org/akhq/utils/FilmProto.java | 1120 +++++++++++++++++ .../utils/ProtobufToJsonDeserializerTest.java | 131 ++ src/test/resources/protobuf_desc/album.desc | 11 + src/test/resources/protobuf_desc/film.desc | 11 + src/test/resources/protobuf_proto/album.proto | 12 + src/test/resources/protobuf_proto/film.proto | 13 + .../generate-descriptor-file.sh | 5 + .../protobuf_proto/generate-java-classes.sh | 8 + 21 files changed, 2726 insertions(+), 13 deletions(-) create mode 100644 src/main/java/org/akhq/configs/TopicsMapping.java create mode 100644 src/main/java/org/akhq/repositories/CustomDeserializerRepository.java create mode 100644 src/main/java/org/akhq/utils/ProtobufToJsonDeserializer.java create mode 100644 src/test/java/org/akhq/utils/Album.java create mode 100644 src/test/java/org/akhq/utils/AlbumProto.java create mode 100644 src/test/java/org/akhq/utils/Film.java create mode 100644 src/test/java/org/akhq/utils/FilmProto.java create mode 100644 src/test/java/org/akhq/utils/ProtobufToJsonDeserializerTest.java create mode 100644 src/test/resources/protobuf_desc/album.desc create mode 100644 src/test/resources/protobuf_desc/film.desc create mode 100644 src/test/resources/protobuf_proto/album.proto create mode 100644 src/test/resources/protobuf_proto/film.proto create mode 100644 src/test/resources/protobuf_proto/generate-descriptor-file.sh create mode 100644 src/test/resources/protobuf_proto/generate-java-classes.sh diff --git a/README.md b/README.md index 23282bc87..b1a3de325 100644 --- a/README.md +++ b/README.md @@ -289,6 +289,40 @@ These parameters are the default values used in the topic creation page. * `akhq.topic-data.poll-timeout`: The time, in milliseconds, spent waiting in poll if data is not available in the buffer (default: 1000). +#### Protobuf deserialization + +To deserialize topics containing data in Protobuf format, you can set topics mapping: +for each `topic-regex` you can specify `descriptor-file-base64` (descriptor file encoded to Base64 format) +and corresponding message types for keys and values. If, for example, keys are not in Protobuf format, +`key-message-type` can be omitted, the same for `value-message-type`. This configuration can be specified +for each Kafka cluster. + +Example configuration can look like as follows: + +``` +akhq: + connections: + kafka: + properties: + # standard kafka properties + deserialization: + protobuf: + topics-mapping: + - topic-regex: "album.*" + descriptor-file-base64: "Cs4BCgthbGJ1bS5wcm90bxIXY29tLm5ldGNyYWNrZXIucHJvdG9idWYidwoFQWxidW0SFAoFdGl0bGUYASABKAlSBXRpdGxlEhYKBmFydGlzdBgCIAMoCVIGYXJ0aXN0EiEKDHJlbGVhc2VfeWVhchgDIAEoBVILcmVsZWFzZVllYXISHQoKc29uZ190aXRsZRgEIAMoCVIJc29uZ1RpdGxlQiUKF2NvbS5uZXRjcmFja2VyLnByb3RvYnVmQgpBbGJ1bVByb3RvYgZwcm90bzM=" + value-message-type: "Album" + - topic-regex: "film.*" + descriptor-file-base64: "CuEBCgpmaWxtLnByb3RvEhRjb20uY29tcGFueS5wcm90b2J1ZiKRAQoERmlsbRISCgRuYW1lGAEgASgJUgRuYW1lEhoKCHByb2R1Y2VyGAIgASgJUghwcm9kdWNlchIhCgxyZWxlYXNlX3llYXIYAyABKAVSC3JlbGVhc2VZZWFyEhoKCGR1cmF0aW9uGAQgASgFUghkdXJhdGlvbhIaCghzdGFycmluZxgFIAMoCVIIc3RhcnJpbmdCIQoUY29tLmNvbXBhbnkucHJvdG9idWZCCUZpbG1Qcm90b2IGcHJvdG8z" + value-message-type: "Film" + - topic-regex: "test.*" + descriptor-file-base64: "Cs4LChhzdHJlYW1pbmctcHJvdG9jb2wucHJvdG8SLmNvbS5uZXRjcmFja2VyLnByb3RvYnVmLnNlcmlhbGl6YXRpb24ucHJvdG9jb2wiIwoFUG9pbnQSDAoBeBgBIAEoAVIBeBIMCgF5GAIgASgBUgF5IscFCgVEYXR1bRIfCgtjb2x1bW5fbmFtZRgBIAEoCVIKY29sdW1uTmFtZRJbCgtjb2x1bW5fdHlwZRgCIAEoDjI6LmNvbS5uZXRjcmFja2VyLnByb3RvYnVmLnNlcmlhbGl6YXRpb24ucHJvdG9jb2wuQ29sdW1uVHlwZVIKY29sdW1uVHlwZRIfCgtzY2hlbWFfbmFtZRgLIAEoCVIKc2NoZW1hTmFtZRJ4ChFzY2hlbWFfcGFyYW1ldGVycxgMIAMoCzJLLmNvbS5uZXRjcmFja2VyLnByb3RvYnVmLnNlcmlhbGl6YXRpb24ucHJvdG9jb2wuRGF0dW0uU2NoZW1hUGFyYW1ldGVyc0VudHJ5UhBzY2hlbWFQYXJhbWV0ZXJzEiUKDWRhdHVtX2ludGVnZXIYAyABKAVIAFIMZGF0dW1JbnRlZ2VyEh8KCmRhdHVtX2xvbmcYBCABKANIAFIJZGF0dW1Mb25nEiEKC2RhdHVtX2Zsb2F0GAUgASgCSABSCmRhdHVtRmxvYXQSIwoMZGF0dW1fZG91YmxlGAYgASgBSABSC2RhdHVtRG91YmxlEiUKDWRhdHVtX2Jvb2xlYW4YByABKAhIAFIMZGF0dW1Cb29sZWFuEiMKDGRhdHVtX3N0cmluZxgIIAEoCUgAUgtkYXR1bVN0cmluZxIhCgtkYXR1bV9ieXRlcxgJIAEoDEgAUgpkYXR1bUJ5dGVzElgKC2RhdHVtX3BvaW50GAogASgLMjUuY29tLm5ldGNyYWNrZXIucHJvdG9idWYuc2VyaWFsaXphdGlvbi5wcm90b2NvbC5Qb2ludEgAUgpkYXR1bVBvaW50GkMKFVNjaGVtYVBhcmFtZXRlcnNFbnRyeRIQCgNrZXkYASABKAlSA2tleRIUCgV2YWx1ZRgCIAEoCVIFdmFsdWU6AjgBQgcKBWRhdHVtIlIKA1JvdxJLCgVkYXR1bRgBIAMoCzI1LmNvbS5uZXRjcmFja2VyLnByb3RvYnVmLnNlcmlhbGl6YXRpb24ucHJvdG9jb2wuRGF0dW1SBWRhdHVtImkKBlNvdXJjZRIcCgljb25uZWN0b3IYASABKAlSCWNvbm5lY3RvchIcCgl0aW1lc3RhbXAYAiABKARSCXRpbWVzdGFtcBIjCg1sYXN0X3NuYXBzaG90GAMgASgIUgxsYXN0U25hcHNob3QijwIKCEVudmVsb3BlEhwKCXRpbWVzdGFtcBgBIAEoBFIJdGltZXN0YW1wEhwKCW9wZXJhdGlvbhgCIAEoCVIJb3BlcmF0aW9uElAKCGRhdGFfcm93GAMgASgLMjMuY29tLm5ldGNyYWNrZXIucHJvdG9idWYuc2VyaWFsaXphdGlvbi5wcm90b2NvbC5Sb3dIAFIHZGF0YVJvdxIdCglkYXRhX2pzb24YBCABKAlIAFIIZGF0YUpzb24STgoGc291cmNlGAUgASgLMjYuY29tLm5ldGNyYWNrZXIucHJvdG9idWYuc2VyaWFsaXphdGlvbi5wcm90b2NvbC5Tb3VyY2VSBnNvdXJjZUIGCgRkYXRhKnMKCkNvbHVtblR5cGUSCwoHSU5URUdFUhAAEggKBExPTkcQARIJCgVGTE9BVBACEgoKBkRPVUJMRRADEgsKB0JPT0xFQU4QBBIKCgZTVFJJTkcQBRIICgRKU09OEAYSCQoFQllURVMQBxIJCgVQT0lOVBAIQkUKLmNvbS5uZXRjcmFja2VyLnByb3RvYnVmLnNlcmlhbGl6YXRpb24ucHJvdG9jb2xCEVN0cmVhbWluZ1Byb3RvY29sSAFiBnByb3RvMw==" + key-message-type: "Row" + value-message-type: "Envelope" +``` + +More examples about Protobuf deserialization can be found in [tests](./src/test/java/org/akhq/utils). +Info about descriptor files generation can be found in [test resources](./src/test/resources/protobuf_proto). + ### Security * `akhq.security.default-group`: Default group for all the user even unlogged user. diff --git a/application.example.yml b/application.example.yml index 96556c7f3..2751a603a 100644 --- a/application.example.yml +++ b/application.example.yml @@ -80,6 +80,19 @@ akhq: ssl-trust-store-password: trust-store-password ssl-key-store: /app/truststore.jks ssl-key-store-password: key-store-password + deserialization: + protobuf: + topics-mapping: + - topic-regex: "album.*" + descriptor-file-base64: "Cs4BCgthbGJ1bS5wcm90bxIXY29tLm5ldGNyYWNrZXIucHJvdG9idWYidwoFQWxidW0SFAoFdGl0bGUYASABKAlSBXRpdGxlEhYKBmFydGlzdBgCIAMoCVIGYXJ0aXN0EiEKDHJlbGVhc2VfeWVhchgDIAEoBVILcmVsZWFzZVllYXISHQoKc29uZ190aXRsZRgEIAMoCVIJc29uZ1RpdGxlQiUKF2NvbS5uZXRjcmFja2VyLnByb3RvYnVmQgpBbGJ1bVByb3RvYgZwcm90bzM=" + value-message-type: "Album" + - topic-regex: "film.*" + descriptor-file-base64: "CuEBCgpmaWxtLnByb3RvEhRjb20uY29tcGFueS5wcm90b2J1ZiKRAQoERmlsbRISCgRuYW1lGAEgASgJUgRuYW1lEhoKCHByb2R1Y2VyGAIgASgJUghwcm9kdWNlchIhCgxyZWxlYXNlX3llYXIYAyABKAVSC3JlbGVhc2VZZWFyEhoKCGR1cmF0aW9uGAQgASgFUghkdXJhdGlvbhIaCghzdGFycmluZxgFIAMoCVIIc3RhcnJpbmdCIQoUY29tLmNvbXBhbnkucHJvdG9idWZCCUZpbG1Qcm90b2IGcHJvdG8z" + value-message-type: "Film" + - topic-regex: "test.*" + descriptor-file-base64: "Cs4LChhzdHJlYW1pbmctcHJvdG9jb2wucHJvdG8SLmNvbS5uZXRjcmFja2VyLnByb3RvYnVmLnNlcmlhbGl6YXRpb24ucHJvdG9jb2wiIwoFUG9pbnQSDAoBeBgBIAEoAVIBeBIMCgF5GAIgASgBUgF5IscFCgVEYXR1bRIfCgtjb2x1bW5fbmFtZRgBIAEoCVIKY29sdW1uTmFtZRJbCgtjb2x1bW5fdHlwZRgCIAEoDjI6LmNvbS5uZXRjcmFja2VyLnByb3RvYnVmLnNlcmlhbGl6YXRpb24ucHJvdG9jb2wuQ29sdW1uVHlwZVIKY29sdW1uVHlwZRIfCgtzY2hlbWFfbmFtZRgLIAEoCVIKc2NoZW1hTmFtZRJ4ChFzY2hlbWFfcGFyYW1ldGVycxgMIAMoCzJLLmNvbS5uZXRjcmFja2VyLnByb3RvYnVmLnNlcmlhbGl6YXRpb24ucHJvdG9jb2wuRGF0dW0uU2NoZW1hUGFyYW1ldGVyc0VudHJ5UhBzY2hlbWFQYXJhbWV0ZXJzEiUKDWRhdHVtX2ludGVnZXIYAyABKAVIAFIMZGF0dW1JbnRlZ2VyEh8KCmRhdHVtX2xvbmcYBCABKANIAFIJZGF0dW1Mb25nEiEKC2RhdHVtX2Zsb2F0GAUgASgCSABSCmRhdHVtRmxvYXQSIwoMZGF0dW1fZG91YmxlGAYgASgBSABSC2RhdHVtRG91YmxlEiUKDWRhdHVtX2Jvb2xlYW4YByABKAhIAFIMZGF0dW1Cb29sZWFuEiMKDGRhdHVtX3N0cmluZxgIIAEoCUgAUgtkYXR1bVN0cmluZxIhCgtkYXR1bV9ieXRlcxgJIAEoDEgAUgpkYXR1bUJ5dGVzElgKC2RhdHVtX3BvaW50GAogASgLMjUuY29tLm5ldGNyYWNrZXIucHJvdG9idWYuc2VyaWFsaXphdGlvbi5wcm90b2NvbC5Qb2ludEgAUgpkYXR1bVBvaW50GkMKFVNjaGVtYVBhcmFtZXRlcnNFbnRyeRIQCgNrZXkYASABKAlSA2tleRIUCgV2YWx1ZRgCIAEoCVIFdmFsdWU6AjgBQgcKBWRhdHVtIlIKA1JvdxJLCgVkYXR1bRgBIAMoCzI1LmNvbS5uZXRjcmFja2VyLnByb3RvYnVmLnNlcmlhbGl6YXRpb24ucHJvdG9jb2wuRGF0dW1SBWRhdHVtImkKBlNvdXJjZRIcCgljb25uZWN0b3IYASABKAlSCWNvbm5lY3RvchIcCgl0aW1lc3RhbXAYAiABKARSCXRpbWVzdGFtcBIjCg1sYXN0X3NuYXBzaG90GAMgASgIUgxsYXN0U25hcHNob3QijwIKCEVudmVsb3BlEhwKCXRpbWVzdGFtcBgBIAEoBFIJdGltZXN0YW1wEhwKCW9wZXJhdGlvbhgCIAEoCVIJb3BlcmF0aW9uElAKCGRhdGFfcm93GAMgASgLMjMuY29tLm5ldGNyYWNrZXIucHJvdG9idWYuc2VyaWFsaXphdGlvbi5wcm90b2NvbC5Sb3dIAFIHZGF0YVJvdxIdCglkYXRhX2pzb24YBCABKAlIAFIIZGF0YUpzb24STgoGc291cmNlGAUgASgLMjYuY29tLm5ldGNyYWNrZXIucHJvdG9idWYuc2VyaWFsaXphdGlvbi5wcm90b2NvbC5Tb3VyY2VSBnNvdXJjZUIGCgRkYXRhKnMKCkNvbHVtblR5cGUSCwoHSU5URUdFUhAAEggKBExPTkcQARIJCgVGTE9BVBACEgoKBkRPVUJMRRADEgsKB0JPT0xFQU4QBBIKCgZTVFJJTkcQBRIICgRKU09OEAYSCQoFQllURVMQBxIJCgVQT0lOVBAIQkUKLmNvbS5uZXRjcmFja2VyLnByb3RvYnVmLnNlcmlhbGl6YXRpb24ucHJvdG9jb2xCEVN0cmVhbWluZ1Byb3RvY29sSAFiBnByb3RvMw==" + key-message-type: "Row" + value-message-type: "Envelope" my-cluster-ssl: properties: diff --git a/build.gradle b/build.gradle index 378b5480b..c069644c0 100644 --- a/build.gradle +++ b/build.gradle @@ -112,6 +112,10 @@ dependencies { // utils implementation group: 'org.codehaus.httpcache4j.uribuilder', name: 'uribuilder', version: '2.0.0' + // protobuf + implementation group: "com.google.protobuf", name: "protobuf-java", version: "3.13.0" + implementation group: "com.google.protobuf", name: "protobuf-java-util", version: "3.13.0" + // Password hashing implementation group: "org.mindrot", name: "jbcrypt", version: "0.4" diff --git a/src/main/java/org/akhq/configs/Connection.java b/src/main/java/org/akhq/configs/Connection.java index bf7f540f9..d8875cc1c 100644 --- a/src/main/java/org/akhq/configs/Connection.java +++ b/src/main/java/org/akhq/configs/Connection.java @@ -4,9 +4,10 @@ import io.micronaut.context.annotation.EachProperty; import io.micronaut.context.annotation.Parameter; import io.micronaut.core.convert.format.MapFormat; +import lombok.Data; import lombok.Getter; -import java.net.URL; +import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -15,6 +16,7 @@ public class Connection extends AbstractProperties { SchemaRegistry schemaRegistry; List connect; + ProtobufDeserializationTopicsMapping deserialization; public Connection(@Parameter String name) { super(name); @@ -30,5 +32,12 @@ public static class SchemaRegistry { @MapFormat(transformation = MapFormat.MapTransformation.FLAT) Map properties; } + + @Getter + @Data + @ConfigurationProperties("deserialization.protobuf") + public static class ProtobufDeserializationTopicsMapping { + List topicsMapping = new ArrayList<>(); + } } diff --git a/src/main/java/org/akhq/configs/TopicsMapping.java b/src/main/java/org/akhq/configs/TopicsMapping.java new file mode 100644 index 000000000..a3dea36e8 --- /dev/null +++ b/src/main/java/org/akhq/configs/TopicsMapping.java @@ -0,0 +1,15 @@ +package org.akhq.configs; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class TopicsMapping { + String topicRegex; + String descriptorFileBase64; + String keyMessageType; + String valueMessageType; +} diff --git a/src/main/java/org/akhq/models/Record.java b/src/main/java/org/akhq/models/Record.java index 528f9ad95..f806c5304 100644 --- a/src/main/java/org/akhq/models/Record.java +++ b/src/main/java/org/akhq/models/Record.java @@ -4,6 +4,7 @@ import io.confluent.kafka.serializers.KafkaAvroDeserializer; import lombok.*; import org.akhq.utils.AvroToJsonSerializer; +import org.akhq.utils.ProtobufToJsonDeserializer; import org.apache.avro.generic.GenericRecord; import org.apache.kafka.clients.consumer.ConsumerRecord; import org.apache.kafka.clients.producer.RecordMetadata; @@ -34,6 +35,7 @@ public class Record { private Map headers = new HashMap<>(); @JsonIgnore private KafkaAvroDeserializer kafkaAvroDeserializer; + private ProtobufToJsonDeserializer protobufToJsonDeserializer; @Getter(AccessLevel.NONE) private byte[] bytesKey; @@ -59,7 +61,8 @@ public Record(RecordMetadata record, byte[] bytesKey, byte[] bytesValue, Map record, KafkaAvroDeserializer kafkaAvroDeserializer, byte[] bytesValue) { + public Record(ConsumerRecord record, KafkaAvroDeserializer kafkaAvroDeserializer, + ProtobufToJsonDeserializer protobufToJsonDeserializer, byte[] bytesValue) { this.topic = record.topic(); this.partition = record.partition(); this.offset = record.offset(); @@ -74,11 +77,12 @@ public Record(ConsumerRecord record, KafkaAvroDeserializer kafka } this.kafkaAvroDeserializer = kafkaAvroDeserializer; + this.protobufToJsonDeserializer = protobufToJsonDeserializer; } public String getKey() { if (this.key == null) { - this.key = convertToString(bytesKey, keySchemaId); + this.key = convertToString(bytesKey, keySchemaId, true); } return this.key; @@ -95,16 +99,16 @@ public String getKeyAsBase64() { public String getValue() { if (this.value == null) { - this.value = convertToString(bytesValue, valueSchemaId); + this.value = convertToString(bytesValue, valueSchemaId, false); } return this.value; } - private String convertToString(byte[] payload, Integer keySchemaId) { + private String convertToString(byte[] payload, Integer schemaId, boolean isKey) { if (payload == null) { return null; - } else if (keySchemaId != null) { + } else if (schemaId != null) { try { GenericRecord record = (GenericRecord) kafkaAvroDeserializer.deserialize(topic, payload); return AvroToJsonSerializer.toJson(record); @@ -112,6 +116,12 @@ private String convertToString(byte[] payload, Integer keySchemaId) { return new String(payload); } } else { + if (protobufToJsonDeserializer != null) { + String record = protobufToJsonDeserializer.deserialize(topic, payload, isKey); + if (record != null) { + return record; + } + } return new String(payload); } } diff --git a/src/main/java/org/akhq/modules/KafkaModule.java b/src/main/java/org/akhq/modules/KafkaModule.java index af4360509..e4797cf1e 100644 --- a/src/main/java/org/akhq/modules/KafkaModule.java +++ b/src/main/java/org/akhq/modules/KafkaModule.java @@ -48,7 +48,7 @@ public List getClustersList() { .collect(Collectors.toList()); } - private Connection getConnection(String cluster) { + public Connection getConnection(String cluster) { return this.connections .stream() .filter(r -> r.getName().equals(cluster)) diff --git a/src/main/java/org/akhq/repositories/CustomDeserializerRepository.java b/src/main/java/org/akhq/repositories/CustomDeserializerRepository.java new file mode 100644 index 000000000..fe84346ac --- /dev/null +++ b/src/main/java/org/akhq/repositories/CustomDeserializerRepository.java @@ -0,0 +1,26 @@ +package org.akhq.repositories; + +import org.akhq.modules.KafkaModule; +import org.akhq.utils.ProtobufToJsonDeserializer; + +import javax.inject.Inject; +import javax.inject.Singleton; +import java.util.HashMap; +import java.util.Map; + +@Singleton +public class CustomDeserializerRepository { + @Inject + private KafkaModule kafkaModule; + private final Map protobufToJsonDeserializers = new HashMap<>(); + + public ProtobufToJsonDeserializer getProtobufToJsonDeserializer(String clusterId) { + if (!this.protobufToJsonDeserializers.containsKey(clusterId)) { + this.protobufToJsonDeserializers.put( + clusterId, + new ProtobufToJsonDeserializer(this.kafkaModule.getConnection(clusterId).getDeserialization()) + ); + } + return this.protobufToJsonDeserializers.get(clusterId); + } +} diff --git a/src/main/java/org/akhq/repositories/RecordRepository.java b/src/main/java/org/akhq/repositories/RecordRepository.java index d96d25319..340e10528 100644 --- a/src/main/java/org/akhq/repositories/RecordRepository.java +++ b/src/main/java/org/akhq/repositories/RecordRepository.java @@ -28,12 +28,6 @@ import org.akhq.modules.AvroSerializer; import org.akhq.modules.KafkaModule; import org.akhq.utils.Debug; -import org.apache.kafka.clients.consumer.*; -import org.apache.kafka.clients.producer.ProducerRecord; -import org.apache.kafka.clients.producer.RecordMetadata; -import org.apache.kafka.common.TopicPartition; -import org.apache.kafka.common.header.internals.RecordHeader; -import org.codehaus.httpcache4j.uri.URIBuilder; import java.util.*; import java.util.concurrent.ExecutionException; @@ -55,6 +49,9 @@ public class RecordRepository extends AbstractRepository { @Inject private SchemaRegistryRepository schemaRegistryRepository; + @Inject + private CustomDeserializerRepository customDeserializerRepository; + @Inject private AvroWireFormatConverter avroWireFormatConverter; @@ -381,6 +378,7 @@ private Record newRecord(ConsumerRecord record, BaseOptions opti return new Record( record, this.schemaRegistryRepository.getKafkaAvroDeserializer(options.clusterId), + this.customDeserializerRepository.getProtobufToJsonDeserializer(options.clusterId), avroWireFormatConverter.convertValueToWireFormat(record, this.kafkaModule.getRegistryClient(options.clusterId)) ); } diff --git a/src/main/java/org/akhq/utils/ProtobufToJsonDeserializer.java b/src/main/java/org/akhq/utils/ProtobufToJsonDeserializer.java new file mode 100644 index 000000000..f8b01ea65 --- /dev/null +++ b/src/main/java/org/akhq/utils/ProtobufToJsonDeserializer.java @@ -0,0 +1,142 @@ +package org.akhq.utils; + +import com.google.protobuf.DescriptorProtos.FileDescriptorProto; +import com.google.protobuf.DescriptorProtos.FileDescriptorSet; +import com.google.protobuf.Descriptors.Descriptor; +import com.google.protobuf.Descriptors.DescriptorValidationException; +import com.google.protobuf.Descriptors.FileDescriptor; +import com.google.protobuf.DynamicMessage; +import com.google.protobuf.util.JsonFormat; +import lombok.extern.slf4j.Slf4j; +import org.akhq.configs.Connection; +import org.akhq.configs.TopicsMapping; + +import java.io.IOException; +import java.util.*; +import java.util.stream.Collectors; + +/** + * Class for deserialization of messages in Protobuf format using topics mapping config. + */ +@Slf4j +public class ProtobufToJsonDeserializer { + private final Connection.ProtobufDeserializationTopicsMapping protobufDeserializationTopicsMapping; + private final Map> descriptors; + private final List topicsMapping; + + public ProtobufToJsonDeserializer(Connection.ProtobufDeserializationTopicsMapping protobufDeserializationTopicsMapping) { + this.protobufDeserializationTopicsMapping = protobufDeserializationTopicsMapping; + if (protobufDeserializationTopicsMapping == null) { + this.descriptors = new HashMap<>(); + this.topicsMapping = new ArrayList<>(); + } else { + this.descriptors = buildAllDescriptors(); + this.topicsMapping = protobufDeserializationTopicsMapping.getTopicsMapping(); + } + } + + /** + * Check protobuf deserialization topics mapping config, get all protobuf descriptor files in Base64 format and convert to bytes + * For each descriptor file builds Descriptors list - full description with all dependencies + * + * @return map where keys are topic regexes and values are Descriptors matching these regexes + */ + private Map> buildAllDescriptors() { + List topicsMapping = protobufDeserializationTopicsMapping.getTopicsMapping(); + Map> allDescriptors = new HashMap<>(); + for (TopicsMapping mapping : topicsMapping) { + byte[] decodedDescriptorFile = Base64.getDecoder().decode(mapping.getDescriptorFileBase64()); + try { + allDescriptors.put(mapping.getTopicRegex(), buildAllDescriptorsForDescriptorFile(decodedDescriptorFile)); + } catch (IOException | DescriptorValidationException e) { + log.error("Cannot build Protobuf descriptors for topics regex [{}]", mapping.getTopicRegex(), e); + } + } + return allDescriptors; + } + + /** + * Builds Descriptors list for current descriptor file + */ + private List buildAllDescriptorsForDescriptorFile(byte[] descriptorFile) + throws IOException, DescriptorValidationException { + FileDescriptorSet fileDescriptorSet = FileDescriptorSet.parseFrom(descriptorFile); + + List fileDescriptorsWithDependencies = new ArrayList<>(); + for (FileDescriptorProto protoDescriptorFile : fileDescriptorSet.getFileList()) { + FileDescriptor fd = FileDescriptor.buildFrom(protoDescriptorFile, + fileDescriptorsWithDependencies.toArray(new FileDescriptor[fileDescriptorsWithDependencies.size()])); + fileDescriptorsWithDependencies.add(fd); + } + + return fileDescriptorsWithDependencies + .stream().flatMap(desc -> desc.getMessageTypes().stream()) + .collect(Collectors.toList()); + } + + /** + * Deserialize binary data from Protobuf format to Json. + * Topic name should match topic-regex from {@code akhq.connections.[clusterName].deserialization.protobuf.topics-mapping} config + * and message-type should be set for key or value in that config. + * + * @param topic current topic name + * @param buffer binary data to decode + * @param isKey is this data represent key or value + * @return {@code null} if cannot deserialize or configuration is not matching, return decoded string otherwise + */ + public String deserialize(String topic, byte[] buffer, boolean isKey) { + TopicsMapping matchingConfig = findMatchingConfig(topic); + if (matchingConfig == null) { + return null; + } + String messageType = matchingConfig.getValueMessageType(); + if (isKey) { + messageType = matchingConfig.getKeyMessageType(); + } + + if (messageType == null) { + return null; + } + + String result = null; + try { + result = tryToDeserializeWithMessageType(buffer, matchingConfig.getTopicRegex(), messageType); + } catch (Exception e) { + log.error("Cannot deserialize message with Protobuf deserializer " + + "for topic [{}] and message type [{}]", topic, messageType, e); + } + return result; + } + + private TopicsMapping findMatchingConfig(String topic) { + for (TopicsMapping mapping : topicsMapping) { + if (topic.matches(mapping.getTopicRegex())) { + return new TopicsMapping(mapping.getTopicRegex(), mapping.getDescriptorFileBase64(), mapping.getKeyMessageType(), mapping.getValueMessageType()); + } + } + return null; + } + + private String tryToDeserializeWithMessageType(byte[] buffer, String topicRegex, String messageType) throws IOException { + List descriptorsWithDependencies = this.descriptors.get(topicRegex); + List descriptorsForConfiguredMessageTypes = + descriptorsWithDependencies.stream() + .filter(mp -> messageType.equals(mp.getName())) + .collect(Collectors.toList()); + + for (Descriptor descriptor : descriptorsForConfiguredMessageTypes) { + String decodedMessage = tryToParseDataToJsonWithDescriptor(buffer, descriptor, descriptorsWithDependencies); + if (!decodedMessage.isEmpty()) { + return decodedMessage; + } + } + return null; + } + + private String tryToParseDataToJsonWithDescriptor(byte[] buffer, Descriptor descriptor, List allDependencies) throws IOException { + DynamicMessage message = DynamicMessage.parseFrom(descriptor, buffer); + JsonFormat.TypeRegistry typeRegistry = JsonFormat.TypeRegistry.newBuilder().add(allDependencies).build(); + JsonFormat.Printer printer = JsonFormat.printer().usingTypeRegistry(typeRegistry); + return printer.print(message); + } +} diff --git a/src/test/java/org/akhq/utils/Album.java b/src/test/java/org/akhq/utils/Album.java new file mode 100644 index 000000000..710abd9a7 --- /dev/null +++ b/src/test/java/org/akhq/utils/Album.java @@ -0,0 +1,15 @@ +package org.akhq.utils; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.List; + +@Getter +@AllArgsConstructor +public class Album { + private final String title; + private final List artists; + private final int releaseYear; + private final List songsTitles; +} diff --git a/src/test/java/org/akhq/utils/AlbumProto.java b/src/test/java/org/akhq/utils/AlbumProto.java new file mode 100644 index 000000000..c461eeaf7 --- /dev/null +++ b/src/test/java/org/akhq/utils/AlbumProto.java @@ -0,0 +1,1120 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: album.proto + +package org.akhq.utils; + +public final class AlbumProto { + private AlbumProto() {} + public static void registerAllExtensions( + com.google.protobuf.ExtensionRegistryLite registry) { + } + + public static void registerAllExtensions( + com.google.protobuf.ExtensionRegistry registry) { + registerAllExtensions( + (com.google.protobuf.ExtensionRegistryLite) registry); + } + public interface AlbumOrBuilder extends + // @@protoc_insertion_point(interface_extends:org.akhq.utils.Album) + com.google.protobuf.MessageOrBuilder { + + /** + * string title = 1; + * @return The title. + */ + java.lang.String getTitle(); + /** + * string title = 1; + * @return The bytes for title. + */ + com.google.protobuf.ByteString + getTitleBytes(); + + /** + * repeated string artist = 2; + * @return A list containing the artist. + */ + java.util.List + getArtistList(); + /** + * repeated string artist = 2; + * @return The count of artist. + */ + int getArtistCount(); + /** + * repeated string artist = 2; + * @param index The index of the element to return. + * @return The artist at the given index. + */ + java.lang.String getArtist(int index); + /** + * repeated string artist = 2; + * @param index The index of the value to return. + * @return The bytes of the artist at the given index. + */ + com.google.protobuf.ByteString + getArtistBytes(int index); + + /** + * int32 release_year = 3; + * @return The releaseYear. + */ + int getReleaseYear(); + + /** + * repeated string song_title = 4; + * @return A list containing the songTitle. + */ + java.util.List + getSongTitleList(); + /** + * repeated string song_title = 4; + * @return The count of songTitle. + */ + int getSongTitleCount(); + /** + * repeated string song_title = 4; + * @param index The index of the element to return. + * @return The songTitle at the given index. + */ + java.lang.String getSongTitle(int index); + /** + * repeated string song_title = 4; + * @param index The index of the value to return. + * @return The bytes of the songTitle at the given index. + */ + com.google.protobuf.ByteString + getSongTitleBytes(int index); + } + /** + * Protobuf type {@code org.akhq.utils.Album} + */ + public static final class Album extends + com.google.protobuf.GeneratedMessageV3 implements + // @@protoc_insertion_point(message_implements:org.akhq.utils.Album) + AlbumOrBuilder { + private static final long serialVersionUID = 0L; + // Use Album.newBuilder() to construct. + private Album(com.google.protobuf.GeneratedMessageV3.Builder builder) { + super(builder); + } + private Album() { + title_ = ""; + artist_ = com.google.protobuf.LazyStringArrayList.EMPTY; + songTitle_ = com.google.protobuf.LazyStringArrayList.EMPTY; + } + + @java.lang.Override + @SuppressWarnings({"unused"}) + protected java.lang.Object newInstance( + UnusedPrivateParameter unused) { + return new Album(); + } + + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private Album( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + this(); + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 10: { + java.lang.String s = input.readStringRequireUtf8(); + + title_ = s; + break; + } + case 18: { + java.lang.String s = input.readStringRequireUtf8(); + if (!((mutable_bitField0_ & 0x00000001) != 0)) { + artist_ = new com.google.protobuf.LazyStringArrayList(); + mutable_bitField0_ |= 0x00000001; + } + artist_.add(s); + break; + } + case 24: { + + releaseYear_ = input.readInt32(); + break; + } + case 34: { + java.lang.String s = input.readStringRequireUtf8(); + if (!((mutable_bitField0_ & 0x00000002) != 0)) { + songTitle_ = new com.google.protobuf.LazyStringArrayList(); + mutable_bitField0_ |= 0x00000002; + } + songTitle_.add(s); + break; + } + default: { + if (!parseUnknownField( + input, unknownFields, extensionRegistry, tag)) { + done = true; + } + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e).setUnfinishedMessage(this); + } finally { + if (((mutable_bitField0_ & 0x00000001) != 0)) { + artist_ = artist_.getUnmodifiableView(); + } + if (((mutable_bitField0_ & 0x00000002) != 0)) { + songTitle_ = songTitle_.getUnmodifiableView(); + } + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.akhq.utils.AlbumProto.internal_static_org_akhq_utils_Album_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.akhq.utils.AlbumProto.internal_static_org_akhq_utils_Album_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.akhq.utils.AlbumProto.Album.class, org.akhq.utils.AlbumProto.Album.Builder.class); + } + + public static final int TITLE_FIELD_NUMBER = 1; + private volatile java.lang.Object title_; + /** + * string title = 1; + * @return The title. + */ + public java.lang.String getTitle() { + java.lang.Object ref = title_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + title_ = s; + return s; + } + } + /** + * string title = 1; + * @return The bytes for title. + */ + public com.google.protobuf.ByteString + getTitleBytes() { + java.lang.Object ref = title_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + title_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int ARTIST_FIELD_NUMBER = 2; + private com.google.protobuf.LazyStringList artist_; + /** + * repeated string artist = 2; + * @return A list containing the artist. + */ + public com.google.protobuf.ProtocolStringList + getArtistList() { + return artist_; + } + /** + * repeated string artist = 2; + * @return The count of artist. + */ + public int getArtistCount() { + return artist_.size(); + } + /** + * repeated string artist = 2; + * @param index The index of the element to return. + * @return The artist at the given index. + */ + public java.lang.String getArtist(int index) { + return artist_.get(index); + } + /** + * repeated string artist = 2; + * @param index The index of the value to return. + * @return The bytes of the artist at the given index. + */ + public com.google.protobuf.ByteString + getArtistBytes(int index) { + return artist_.getByteString(index); + } + + public static final int RELEASE_YEAR_FIELD_NUMBER = 3; + private int releaseYear_; + /** + * int32 release_year = 3; + * @return The releaseYear. + */ + public int getReleaseYear() { + return releaseYear_; + } + + public static final int SONG_TITLE_FIELD_NUMBER = 4; + private com.google.protobuf.LazyStringList songTitle_; + /** + * repeated string song_title = 4; + * @return A list containing the songTitle. + */ + public com.google.protobuf.ProtocolStringList + getSongTitleList() { + return songTitle_; + } + /** + * repeated string song_title = 4; + * @return The count of songTitle. + */ + public int getSongTitleCount() { + return songTitle_.size(); + } + /** + * repeated string song_title = 4; + * @param index The index of the element to return. + * @return The songTitle at the given index. + */ + public java.lang.String getSongTitle(int index) { + return songTitle_.get(index); + } + /** + * repeated string song_title = 4; + * @param index The index of the value to return. + * @return The bytes of the songTitle at the given index. + */ + public com.google.protobuf.ByteString + getSongTitleBytes(int index) { + return songTitle_.getByteString(index); + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + if (!getTitleBytes().isEmpty()) { + com.google.protobuf.GeneratedMessageV3.writeString(output, 1, title_); + } + for (int i = 0; i < artist_.size(); i++) { + com.google.protobuf.GeneratedMessageV3.writeString(output, 2, artist_.getRaw(i)); + } + if (releaseYear_ != 0) { + output.writeInt32(3, releaseYear_); + } + for (int i = 0; i < songTitle_.size(); i++) { + com.google.protobuf.GeneratedMessageV3.writeString(output, 4, songTitle_.getRaw(i)); + } + unknownFields.writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (!getTitleBytes().isEmpty()) { + size += com.google.protobuf.GeneratedMessageV3.computeStringSize(1, title_); + } + { + int dataSize = 0; + for (int i = 0; i < artist_.size(); i++) { + dataSize += computeStringSizeNoTag(artist_.getRaw(i)); + } + size += dataSize; + size += 1 * getArtistList().size(); + } + if (releaseYear_ != 0) { + size += com.google.protobuf.CodedOutputStream + .computeInt32Size(3, releaseYear_); + } + { + int dataSize = 0; + for (int i = 0; i < songTitle_.size(); i++) { + dataSize += computeStringSizeNoTag(songTitle_.getRaw(i)); + } + size += dataSize; + size += 1 * getSongTitleList().size(); + } + size += unknownFields.getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof org.akhq.utils.AlbumProto.Album)) { + return super.equals(obj); + } + org.akhq.utils.AlbumProto.Album other = (org.akhq.utils.AlbumProto.Album) obj; + + if (!getTitle() + .equals(other.getTitle())) return false; + if (!getArtistList() + .equals(other.getArtistList())) return false; + if (getReleaseYear() + != other.getReleaseYear()) return false; + if (!getSongTitleList() + .equals(other.getSongTitleList())) return false; + if (!unknownFields.equals(other.unknownFields)) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + hash = (37 * hash) + TITLE_FIELD_NUMBER; + hash = (53 * hash) + getTitle().hashCode(); + if (getArtistCount() > 0) { + hash = (37 * hash) + ARTIST_FIELD_NUMBER; + hash = (53 * hash) + getArtistList().hashCode(); + } + hash = (37 * hash) + RELEASE_YEAR_FIELD_NUMBER; + hash = (53 * hash) + getReleaseYear(); + if (getSongTitleCount() > 0) { + hash = (37 * hash) + SONG_TITLE_FIELD_NUMBER; + hash = (53 * hash) + getSongTitleList().hashCode(); + } + hash = (29 * hash) + unknownFields.hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static org.akhq.utils.AlbumProto.Album parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.akhq.utils.AlbumProto.Album parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.akhq.utils.AlbumProto.Album parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.akhq.utils.AlbumProto.Album parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.akhq.utils.AlbumProto.Album parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.akhq.utils.AlbumProto.Album parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.akhq.utils.AlbumProto.Album parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static org.akhq.utils.AlbumProto.Album parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + public static org.akhq.utils.AlbumProto.Album parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input); + } + public static org.akhq.utils.AlbumProto.Album parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static org.akhq.utils.AlbumProto.Album parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static org.akhq.utils.AlbumProto.Album parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(org.akhq.utils.AlbumProto.Album prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code org.akhq.utils.Album} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessageV3.Builder implements + // @@protoc_insertion_point(builder_implements:org.akhq.utils.Album) + org.akhq.utils.AlbumProto.AlbumOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.akhq.utils.AlbumProto.internal_static_org_akhq_utils_Album_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.akhq.utils.AlbumProto.internal_static_org_akhq_utils_Album_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.akhq.utils.AlbumProto.Album.class, org.akhq.utils.AlbumProto.Album.Builder.class); + } + + // Construct using org.akhq.utils.AlbumProto.Album.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessageV3 + .alwaysUseFieldBuilders) { + } + } + @java.lang.Override + public Builder clear() { + super.clear(); + title_ = ""; + + artist_ = com.google.protobuf.LazyStringArrayList.EMPTY; + bitField0_ = (bitField0_ & ~0x00000001); + releaseYear_ = 0; + + songTitle_ = com.google.protobuf.LazyStringArrayList.EMPTY; + bitField0_ = (bitField0_ & ~0x00000002); + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.akhq.utils.AlbumProto.internal_static_org_akhq_utils_Album_descriptor; + } + + @java.lang.Override + public org.akhq.utils.AlbumProto.Album getDefaultInstanceForType() { + return org.akhq.utils.AlbumProto.Album.getDefaultInstance(); + } + + @java.lang.Override + public org.akhq.utils.AlbumProto.Album build() { + org.akhq.utils.AlbumProto.Album result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public org.akhq.utils.AlbumProto.Album buildPartial() { + org.akhq.utils.AlbumProto.Album result = new org.akhq.utils.AlbumProto.Album(this); + int from_bitField0_ = bitField0_; + result.title_ = title_; + if (((bitField0_ & 0x00000001) != 0)) { + artist_ = artist_.getUnmodifiableView(); + bitField0_ = (bitField0_ & ~0x00000001); + } + result.artist_ = artist_; + result.releaseYear_ = releaseYear_; + if (((bitField0_ & 0x00000002) != 0)) { + songTitle_ = songTitle_.getUnmodifiableView(); + bitField0_ = (bitField0_ & ~0x00000002); + } + result.songTitle_ = songTitle_; + onBuilt(); + return result; + } + + @java.lang.Override + public Builder clone() { + return super.clone(); + } + @java.lang.Override + public Builder setField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.setField(field, value); + } + @java.lang.Override + public Builder clearField( + com.google.protobuf.Descriptors.FieldDescriptor field) { + return super.clearField(field); + } + @java.lang.Override + public Builder clearOneof( + com.google.protobuf.Descriptors.OneofDescriptor oneof) { + return super.clearOneof(oneof); + } + @java.lang.Override + public Builder setRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + int index, java.lang.Object value) { + return super.setRepeatedField(field, index, value); + } + @java.lang.Override + public Builder addRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.addRepeatedField(field, value); + } + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.akhq.utils.AlbumProto.Album) { + return mergeFrom((org.akhq.utils.AlbumProto.Album)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.akhq.utils.AlbumProto.Album other) { + if (other == org.akhq.utils.AlbumProto.Album.getDefaultInstance()) return this; + if (!other.getTitle().isEmpty()) { + title_ = other.title_; + onChanged(); + } + if (!other.artist_.isEmpty()) { + if (artist_.isEmpty()) { + artist_ = other.artist_; + bitField0_ = (bitField0_ & ~0x00000001); + } else { + ensureArtistIsMutable(); + artist_.addAll(other.artist_); + } + onChanged(); + } + if (other.getReleaseYear() != 0) { + setReleaseYear(other.getReleaseYear()); + } + if (!other.songTitle_.isEmpty()) { + if (songTitle_.isEmpty()) { + songTitle_ = other.songTitle_; + bitField0_ = (bitField0_ & ~0x00000002); + } else { + ensureSongTitleIsMutable(); + songTitle_.addAll(other.songTitle_); + } + onChanged(); + } + this.mergeUnknownFields(other.unknownFields); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.akhq.utils.AlbumProto.Album parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.akhq.utils.AlbumProto.Album) e.getUnfinishedMessage(); + throw e.unwrapIOException(); + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + private java.lang.Object title_ = ""; + /** + * string title = 1; + * @return The title. + */ + public java.lang.String getTitle() { + java.lang.Object ref = title_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + title_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + * string title = 1; + * @return The bytes for title. + */ + public com.google.protobuf.ByteString + getTitleBytes() { + java.lang.Object ref = title_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + title_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * string title = 1; + * @param value The title to set. + * @return This builder for chaining. + */ + public Builder setTitle( + java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + + title_ = value; + onChanged(); + return this; + } + /** + * string title = 1; + * @return This builder for chaining. + */ + public Builder clearTitle() { + + title_ = getDefaultInstance().getTitle(); + onChanged(); + return this; + } + /** + * string title = 1; + * @param value The bytes for title to set. + * @return This builder for chaining. + */ + public Builder setTitleBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + checkByteStringIsUtf8(value); + + title_ = value; + onChanged(); + return this; + } + + private com.google.protobuf.LazyStringList artist_ = com.google.protobuf.LazyStringArrayList.EMPTY; + private void ensureArtistIsMutable() { + if (!((bitField0_ & 0x00000001) != 0)) { + artist_ = new com.google.protobuf.LazyStringArrayList(artist_); + bitField0_ |= 0x00000001; + } + } + /** + * repeated string artist = 2; + * @return A list containing the artist. + */ + public com.google.protobuf.ProtocolStringList + getArtistList() { + return artist_.getUnmodifiableView(); + } + /** + * repeated string artist = 2; + * @return The count of artist. + */ + public int getArtistCount() { + return artist_.size(); + } + /** + * repeated string artist = 2; + * @param index The index of the element to return. + * @return The artist at the given index. + */ + public java.lang.String getArtist(int index) { + return artist_.get(index); + } + /** + * repeated string artist = 2; + * @param index The index of the value to return. + * @return The bytes of the artist at the given index. + */ + public com.google.protobuf.ByteString + getArtistBytes(int index) { + return artist_.getByteString(index); + } + /** + * repeated string artist = 2; + * @param index The index to set the value at. + * @param value The artist to set. + * @return This builder for chaining. + */ + public Builder setArtist( + int index, java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + ensureArtistIsMutable(); + artist_.set(index, value); + onChanged(); + return this; + } + /** + * repeated string artist = 2; + * @param value The artist to add. + * @return This builder for chaining. + */ + public Builder addArtist( + java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + ensureArtistIsMutable(); + artist_.add(value); + onChanged(); + return this; + } + /** + * repeated string artist = 2; + * @param values The artist to add. + * @return This builder for chaining. + */ + public Builder addAllArtist( + java.lang.Iterable values) { + ensureArtistIsMutable(); + com.google.protobuf.AbstractMessageLite.Builder.addAll( + values, artist_); + onChanged(); + return this; + } + /** + * repeated string artist = 2; + * @return This builder for chaining. + */ + public Builder clearArtist() { + artist_ = com.google.protobuf.LazyStringArrayList.EMPTY; + bitField0_ = (bitField0_ & ~0x00000001); + onChanged(); + return this; + } + /** + * repeated string artist = 2; + * @param value The bytes of the artist to add. + * @return This builder for chaining. + */ + public Builder addArtistBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + checkByteStringIsUtf8(value); + ensureArtistIsMutable(); + artist_.add(value); + onChanged(); + return this; + } + + private int releaseYear_ ; + /** + * int32 release_year = 3; + * @return The releaseYear. + */ + public int getReleaseYear() { + return releaseYear_; + } + /** + * int32 release_year = 3; + * @param value The releaseYear to set. + * @return This builder for chaining. + */ + public Builder setReleaseYear(int value) { + + releaseYear_ = value; + onChanged(); + return this; + } + /** + * int32 release_year = 3; + * @return This builder for chaining. + */ + public Builder clearReleaseYear() { + + releaseYear_ = 0; + onChanged(); + return this; + } + + private com.google.protobuf.LazyStringList songTitle_ = com.google.protobuf.LazyStringArrayList.EMPTY; + private void ensureSongTitleIsMutable() { + if (!((bitField0_ & 0x00000002) != 0)) { + songTitle_ = new com.google.protobuf.LazyStringArrayList(songTitle_); + bitField0_ |= 0x00000002; + } + } + /** + * repeated string song_title = 4; + * @return A list containing the songTitle. + */ + public com.google.protobuf.ProtocolStringList + getSongTitleList() { + return songTitle_.getUnmodifiableView(); + } + /** + * repeated string song_title = 4; + * @return The count of songTitle. + */ + public int getSongTitleCount() { + return songTitle_.size(); + } + /** + * repeated string song_title = 4; + * @param index The index of the element to return. + * @return The songTitle at the given index. + */ + public java.lang.String getSongTitle(int index) { + return songTitle_.get(index); + } + /** + * repeated string song_title = 4; + * @param index The index of the value to return. + * @return The bytes of the songTitle at the given index. + */ + public com.google.protobuf.ByteString + getSongTitleBytes(int index) { + return songTitle_.getByteString(index); + } + /** + * repeated string song_title = 4; + * @param index The index to set the value at. + * @param value The songTitle to set. + * @return This builder for chaining. + */ + public Builder setSongTitle( + int index, java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + ensureSongTitleIsMutable(); + songTitle_.set(index, value); + onChanged(); + return this; + } + /** + * repeated string song_title = 4; + * @param value The songTitle to add. + * @return This builder for chaining. + */ + public Builder addSongTitle( + java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + ensureSongTitleIsMutable(); + songTitle_.add(value); + onChanged(); + return this; + } + /** + * repeated string song_title = 4; + * @param values The songTitle to add. + * @return This builder for chaining. + */ + public Builder addAllSongTitle( + java.lang.Iterable values) { + ensureSongTitleIsMutable(); + com.google.protobuf.AbstractMessageLite.Builder.addAll( + values, songTitle_); + onChanged(); + return this; + } + /** + * repeated string song_title = 4; + * @return This builder for chaining. + */ + public Builder clearSongTitle() { + songTitle_ = com.google.protobuf.LazyStringArrayList.EMPTY; + bitField0_ = (bitField0_ & ~0x00000002); + onChanged(); + return this; + } + /** + * repeated string song_title = 4; + * @param value The bytes of the songTitle to add. + * @return This builder for chaining. + */ + public Builder addSongTitleBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + checkByteStringIsUtf8(value); + ensureSongTitleIsMutable(); + songTitle_.add(value); + onChanged(); + return this; + } + @java.lang.Override + public final Builder setUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.setUnknownFields(unknownFields); + } + + @java.lang.Override + public final Builder mergeUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.mergeUnknownFields(unknownFields); + } + + + // @@protoc_insertion_point(builder_scope:org.akhq.utils.Album) + } + + // @@protoc_insertion_point(class_scope:org.akhq.utils.Album) + private static final org.akhq.utils.AlbumProto.Album DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new org.akhq.utils.AlbumProto.Album(); + } + + public static org.akhq.utils.AlbumProto.Album getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser + PARSER = new com.google.protobuf.AbstractParser() { + @java.lang.Override + public Album parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new Album(input, extensionRegistry); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @java.lang.Override + public org.akhq.utils.AlbumProto.Album getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + + } + + private static final com.google.protobuf.Descriptors.Descriptor + internal_static_org_akhq_utils_Album_descriptor; + private static final + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internal_static_org_akhq_utils_Album_fieldAccessorTable; + + public static com.google.protobuf.Descriptors.FileDescriptor + getDescriptor() { + return descriptor; + } + private static com.google.protobuf.Descriptors.FileDescriptor + descriptor; + static { + java.lang.String[] descriptorData = { + "\n\013album.proto\022\016org.akhq.utils\"P\n\005Album\022\r" + + "\n\005title\030\001 \001(\t\022\016\n\006artist\030\002 \003(\t\022\024\n\014release" + + "_year\030\003 \001(\005\022\022\n\nsong_title\030\004 \003(\tB\034\n\016org.a" + + "khq.utilsB\nAlbumProtob\006proto3" + }; + descriptor = com.google.protobuf.Descriptors.FileDescriptor + .internalBuildGeneratedFileFrom(descriptorData, + new com.google.protobuf.Descriptors.FileDescriptor[] { + }); + internal_static_org_akhq_utils_Album_descriptor = + getDescriptor().getMessageTypes().get(0); + internal_static_org_akhq_utils_Album_fieldAccessorTable = new + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( + internal_static_org_akhq_utils_Album_descriptor, + new java.lang.String[] { "Title", "Artist", "ReleaseYear", "SongTitle", }); + } + + // @@protoc_insertion_point(outer_class_scope) +} diff --git a/src/test/java/org/akhq/utils/Film.java b/src/test/java/org/akhq/utils/Film.java new file mode 100644 index 000000000..62fbc1ac9 --- /dev/null +++ b/src/test/java/org/akhq/utils/Film.java @@ -0,0 +1,16 @@ +package org.akhq.utils; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.List; + +@Getter +@AllArgsConstructor +public class Film { + private final String name; + private final String producer; + private final int releaseYear; + private final int duration; + private final List starring; +} diff --git a/src/test/java/org/akhq/utils/FilmProto.java b/src/test/java/org/akhq/utils/FilmProto.java new file mode 100644 index 000000000..0934ad438 --- /dev/null +++ b/src/test/java/org/akhq/utils/FilmProto.java @@ -0,0 +1,1120 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: film.proto + +package org.akhq.utils; + +public final class FilmProto { + private FilmProto() {} + public static void registerAllExtensions( + com.google.protobuf.ExtensionRegistryLite registry) { + } + + public static void registerAllExtensions( + com.google.protobuf.ExtensionRegistry registry) { + registerAllExtensions( + (com.google.protobuf.ExtensionRegistryLite) registry); + } + public interface FilmOrBuilder extends + // @@protoc_insertion_point(interface_extends:org.akhq.utils.Film) + com.google.protobuf.MessageOrBuilder { + + /** + * string name = 1; + * @return The name. + */ + java.lang.String getName(); + /** + * string name = 1; + * @return The bytes for name. + */ + com.google.protobuf.ByteString + getNameBytes(); + + /** + * string producer = 2; + * @return The producer. + */ + java.lang.String getProducer(); + /** + * string producer = 2; + * @return The bytes for producer. + */ + com.google.protobuf.ByteString + getProducerBytes(); + + /** + * int32 release_year = 3; + * @return The releaseYear. + */ + int getReleaseYear(); + + /** + * int32 duration = 4; + * @return The duration. + */ + int getDuration(); + + /** + * repeated string starring = 5; + * @return A list containing the starring. + */ + java.util.List + getStarringList(); + /** + * repeated string starring = 5; + * @return The count of starring. + */ + int getStarringCount(); + /** + * repeated string starring = 5; + * @param index The index of the element to return. + * @return The starring at the given index. + */ + java.lang.String getStarring(int index); + /** + * repeated string starring = 5; + * @param index The index of the value to return. + * @return The bytes of the starring at the given index. + */ + com.google.protobuf.ByteString + getStarringBytes(int index); + } + /** + * Protobuf type {@code org.akhq.utils.Film} + */ + public static final class Film extends + com.google.protobuf.GeneratedMessageV3 implements + // @@protoc_insertion_point(message_implements:org.akhq.utils.Film) + FilmOrBuilder { + private static final long serialVersionUID = 0L; + // Use Film.newBuilder() to construct. + private Film(com.google.protobuf.GeneratedMessageV3.Builder builder) { + super(builder); + } + private Film() { + name_ = ""; + producer_ = ""; + starring_ = com.google.protobuf.LazyStringArrayList.EMPTY; + } + + @java.lang.Override + @SuppressWarnings({"unused"}) + protected java.lang.Object newInstance( + UnusedPrivateParameter unused) { + return new Film(); + } + + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private Film( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + this(); + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 10: { + java.lang.String s = input.readStringRequireUtf8(); + + name_ = s; + break; + } + case 18: { + java.lang.String s = input.readStringRequireUtf8(); + + producer_ = s; + break; + } + case 24: { + + releaseYear_ = input.readInt32(); + break; + } + case 32: { + + duration_ = input.readInt32(); + break; + } + case 42: { + java.lang.String s = input.readStringRequireUtf8(); + if (!((mutable_bitField0_ & 0x00000001) != 0)) { + starring_ = new com.google.protobuf.LazyStringArrayList(); + mutable_bitField0_ |= 0x00000001; + } + starring_.add(s); + break; + } + default: { + if (!parseUnknownField( + input, unknownFields, extensionRegistry, tag)) { + done = true; + } + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e).setUnfinishedMessage(this); + } finally { + if (((mutable_bitField0_ & 0x00000001) != 0)) { + starring_ = starring_.getUnmodifiableView(); + } + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.akhq.utils.FilmProto.internal_static_org_akhq_utils_Film_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.akhq.utils.FilmProto.internal_static_org_akhq_utils_Film_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.akhq.utils.FilmProto.Film.class, org.akhq.utils.FilmProto.Film.Builder.class); + } + + public static final int NAME_FIELD_NUMBER = 1; + private volatile java.lang.Object name_; + /** + * string name = 1; + * @return The name. + */ + public java.lang.String getName() { + java.lang.Object ref = name_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + name_ = s; + return s; + } + } + /** + * string name = 1; + * @return The bytes for name. + */ + public com.google.protobuf.ByteString + getNameBytes() { + java.lang.Object ref = name_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + name_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int PRODUCER_FIELD_NUMBER = 2; + private volatile java.lang.Object producer_; + /** + * string producer = 2; + * @return The producer. + */ + public java.lang.String getProducer() { + java.lang.Object ref = producer_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + producer_ = s; + return s; + } + } + /** + * string producer = 2; + * @return The bytes for producer. + */ + public com.google.protobuf.ByteString + getProducerBytes() { + java.lang.Object ref = producer_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + producer_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int RELEASE_YEAR_FIELD_NUMBER = 3; + private int releaseYear_; + /** + * int32 release_year = 3; + * @return The releaseYear. + */ + public int getReleaseYear() { + return releaseYear_; + } + + public static final int DURATION_FIELD_NUMBER = 4; + private int duration_; + /** + * int32 duration = 4; + * @return The duration. + */ + public int getDuration() { + return duration_; + } + + public static final int STARRING_FIELD_NUMBER = 5; + private com.google.protobuf.LazyStringList starring_; + /** + * repeated string starring = 5; + * @return A list containing the starring. + */ + public com.google.protobuf.ProtocolStringList + getStarringList() { + return starring_; + } + /** + * repeated string starring = 5; + * @return The count of starring. + */ + public int getStarringCount() { + return starring_.size(); + } + /** + * repeated string starring = 5; + * @param index The index of the element to return. + * @return The starring at the given index. + */ + public java.lang.String getStarring(int index) { + return starring_.get(index); + } + /** + * repeated string starring = 5; + * @param index The index of the value to return. + * @return The bytes of the starring at the given index. + */ + public com.google.protobuf.ByteString + getStarringBytes(int index) { + return starring_.getByteString(index); + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + if (!getNameBytes().isEmpty()) { + com.google.protobuf.GeneratedMessageV3.writeString(output, 1, name_); + } + if (!getProducerBytes().isEmpty()) { + com.google.protobuf.GeneratedMessageV3.writeString(output, 2, producer_); + } + if (releaseYear_ != 0) { + output.writeInt32(3, releaseYear_); + } + if (duration_ != 0) { + output.writeInt32(4, duration_); + } + for (int i = 0; i < starring_.size(); i++) { + com.google.protobuf.GeneratedMessageV3.writeString(output, 5, starring_.getRaw(i)); + } + unknownFields.writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (!getNameBytes().isEmpty()) { + size += com.google.protobuf.GeneratedMessageV3.computeStringSize(1, name_); + } + if (!getProducerBytes().isEmpty()) { + size += com.google.protobuf.GeneratedMessageV3.computeStringSize(2, producer_); + } + if (releaseYear_ != 0) { + size += com.google.protobuf.CodedOutputStream + .computeInt32Size(3, releaseYear_); + } + if (duration_ != 0) { + size += com.google.protobuf.CodedOutputStream + .computeInt32Size(4, duration_); + } + { + int dataSize = 0; + for (int i = 0; i < starring_.size(); i++) { + dataSize += computeStringSizeNoTag(starring_.getRaw(i)); + } + size += dataSize; + size += 1 * getStarringList().size(); + } + size += unknownFields.getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof org.akhq.utils.FilmProto.Film)) { + return super.equals(obj); + } + org.akhq.utils.FilmProto.Film other = (org.akhq.utils.FilmProto.Film) obj; + + if (!getName() + .equals(other.getName())) return false; + if (!getProducer() + .equals(other.getProducer())) return false; + if (getReleaseYear() + != other.getReleaseYear()) return false; + if (getDuration() + != other.getDuration()) return false; + if (!getStarringList() + .equals(other.getStarringList())) return false; + if (!unknownFields.equals(other.unknownFields)) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + hash = (37 * hash) + NAME_FIELD_NUMBER; + hash = (53 * hash) + getName().hashCode(); + hash = (37 * hash) + PRODUCER_FIELD_NUMBER; + hash = (53 * hash) + getProducer().hashCode(); + hash = (37 * hash) + RELEASE_YEAR_FIELD_NUMBER; + hash = (53 * hash) + getReleaseYear(); + hash = (37 * hash) + DURATION_FIELD_NUMBER; + hash = (53 * hash) + getDuration(); + if (getStarringCount() > 0) { + hash = (37 * hash) + STARRING_FIELD_NUMBER; + hash = (53 * hash) + getStarringList().hashCode(); + } + hash = (29 * hash) + unknownFields.hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static org.akhq.utils.FilmProto.Film parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.akhq.utils.FilmProto.Film parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.akhq.utils.FilmProto.Film parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.akhq.utils.FilmProto.Film parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.akhq.utils.FilmProto.Film parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.akhq.utils.FilmProto.Film parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.akhq.utils.FilmProto.Film parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static org.akhq.utils.FilmProto.Film parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + public static org.akhq.utils.FilmProto.Film parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input); + } + public static org.akhq.utils.FilmProto.Film parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static org.akhq.utils.FilmProto.Film parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static org.akhq.utils.FilmProto.Film parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(org.akhq.utils.FilmProto.Film prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code org.akhq.utils.Film} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessageV3.Builder implements + // @@protoc_insertion_point(builder_implements:org.akhq.utils.Film) + org.akhq.utils.FilmProto.FilmOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.akhq.utils.FilmProto.internal_static_org_akhq_utils_Film_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.akhq.utils.FilmProto.internal_static_org_akhq_utils_Film_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.akhq.utils.FilmProto.Film.class, org.akhq.utils.FilmProto.Film.Builder.class); + } + + // Construct using org.akhq.utils.FilmProto.Film.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessageV3 + .alwaysUseFieldBuilders) { + } + } + @java.lang.Override + public Builder clear() { + super.clear(); + name_ = ""; + + producer_ = ""; + + releaseYear_ = 0; + + duration_ = 0; + + starring_ = com.google.protobuf.LazyStringArrayList.EMPTY; + bitField0_ = (bitField0_ & ~0x00000001); + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.akhq.utils.FilmProto.internal_static_org_akhq_utils_Film_descriptor; + } + + @java.lang.Override + public org.akhq.utils.FilmProto.Film getDefaultInstanceForType() { + return org.akhq.utils.FilmProto.Film.getDefaultInstance(); + } + + @java.lang.Override + public org.akhq.utils.FilmProto.Film build() { + org.akhq.utils.FilmProto.Film result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public org.akhq.utils.FilmProto.Film buildPartial() { + org.akhq.utils.FilmProto.Film result = new org.akhq.utils.FilmProto.Film(this); + int from_bitField0_ = bitField0_; + result.name_ = name_; + result.producer_ = producer_; + result.releaseYear_ = releaseYear_; + result.duration_ = duration_; + if (((bitField0_ & 0x00000001) != 0)) { + starring_ = starring_.getUnmodifiableView(); + bitField0_ = (bitField0_ & ~0x00000001); + } + result.starring_ = starring_; + onBuilt(); + return result; + } + + @java.lang.Override + public Builder clone() { + return super.clone(); + } + @java.lang.Override + public Builder setField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.setField(field, value); + } + @java.lang.Override + public Builder clearField( + com.google.protobuf.Descriptors.FieldDescriptor field) { + return super.clearField(field); + } + @java.lang.Override + public Builder clearOneof( + com.google.protobuf.Descriptors.OneofDescriptor oneof) { + return super.clearOneof(oneof); + } + @java.lang.Override + public Builder setRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + int index, java.lang.Object value) { + return super.setRepeatedField(field, index, value); + } + @java.lang.Override + public Builder addRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.addRepeatedField(field, value); + } + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.akhq.utils.FilmProto.Film) { + return mergeFrom((org.akhq.utils.FilmProto.Film)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.akhq.utils.FilmProto.Film other) { + if (other == org.akhq.utils.FilmProto.Film.getDefaultInstance()) return this; + if (!other.getName().isEmpty()) { + name_ = other.name_; + onChanged(); + } + if (!other.getProducer().isEmpty()) { + producer_ = other.producer_; + onChanged(); + } + if (other.getReleaseYear() != 0) { + setReleaseYear(other.getReleaseYear()); + } + if (other.getDuration() != 0) { + setDuration(other.getDuration()); + } + if (!other.starring_.isEmpty()) { + if (starring_.isEmpty()) { + starring_ = other.starring_; + bitField0_ = (bitField0_ & ~0x00000001); + } else { + ensureStarringIsMutable(); + starring_.addAll(other.starring_); + } + onChanged(); + } + this.mergeUnknownFields(other.unknownFields); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.akhq.utils.FilmProto.Film parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.akhq.utils.FilmProto.Film) e.getUnfinishedMessage(); + throw e.unwrapIOException(); + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + private java.lang.Object name_ = ""; + /** + * string name = 1; + * @return The name. + */ + public java.lang.String getName() { + java.lang.Object ref = name_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + name_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + * string name = 1; + * @return The bytes for name. + */ + public com.google.protobuf.ByteString + getNameBytes() { + java.lang.Object ref = name_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + name_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * string name = 1; + * @param value The name to set. + * @return This builder for chaining. + */ + public Builder setName( + java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + + name_ = value; + onChanged(); + return this; + } + /** + * string name = 1; + * @return This builder for chaining. + */ + public Builder clearName() { + + name_ = getDefaultInstance().getName(); + onChanged(); + return this; + } + /** + * string name = 1; + * @param value The bytes for name to set. + * @return This builder for chaining. + */ + public Builder setNameBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + checkByteStringIsUtf8(value); + + name_ = value; + onChanged(); + return this; + } + + private java.lang.Object producer_ = ""; + /** + * string producer = 2; + * @return The producer. + */ + public java.lang.String getProducer() { + java.lang.Object ref = producer_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + producer_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + * string producer = 2; + * @return The bytes for producer. + */ + public com.google.protobuf.ByteString + getProducerBytes() { + java.lang.Object ref = producer_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + producer_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * string producer = 2; + * @param value The producer to set. + * @return This builder for chaining. + */ + public Builder setProducer( + java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + + producer_ = value; + onChanged(); + return this; + } + /** + * string producer = 2; + * @return This builder for chaining. + */ + public Builder clearProducer() { + + producer_ = getDefaultInstance().getProducer(); + onChanged(); + return this; + } + /** + * string producer = 2; + * @param value The bytes for producer to set. + * @return This builder for chaining. + */ + public Builder setProducerBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + checkByteStringIsUtf8(value); + + producer_ = value; + onChanged(); + return this; + } + + private int releaseYear_ ; + /** + * int32 release_year = 3; + * @return The releaseYear. + */ + public int getReleaseYear() { + return releaseYear_; + } + /** + * int32 release_year = 3; + * @param value The releaseYear to set. + * @return This builder for chaining. + */ + public Builder setReleaseYear(int value) { + + releaseYear_ = value; + onChanged(); + return this; + } + /** + * int32 release_year = 3; + * @return This builder for chaining. + */ + public Builder clearReleaseYear() { + + releaseYear_ = 0; + onChanged(); + return this; + } + + private int duration_ ; + /** + * int32 duration = 4; + * @return The duration. + */ + public int getDuration() { + return duration_; + } + /** + * int32 duration = 4; + * @param value The duration to set. + * @return This builder for chaining. + */ + public Builder setDuration(int value) { + + duration_ = value; + onChanged(); + return this; + } + /** + * int32 duration = 4; + * @return This builder for chaining. + */ + public Builder clearDuration() { + + duration_ = 0; + onChanged(); + return this; + } + + private com.google.protobuf.LazyStringList starring_ = com.google.protobuf.LazyStringArrayList.EMPTY; + private void ensureStarringIsMutable() { + if (!((bitField0_ & 0x00000001) != 0)) { + starring_ = new com.google.protobuf.LazyStringArrayList(starring_); + bitField0_ |= 0x00000001; + } + } + /** + * repeated string starring = 5; + * @return A list containing the starring. + */ + public com.google.protobuf.ProtocolStringList + getStarringList() { + return starring_.getUnmodifiableView(); + } + /** + * repeated string starring = 5; + * @return The count of starring. + */ + public int getStarringCount() { + return starring_.size(); + } + /** + * repeated string starring = 5; + * @param index The index of the element to return. + * @return The starring at the given index. + */ + public java.lang.String getStarring(int index) { + return starring_.get(index); + } + /** + * repeated string starring = 5; + * @param index The index of the value to return. + * @return The bytes of the starring at the given index. + */ + public com.google.protobuf.ByteString + getStarringBytes(int index) { + return starring_.getByteString(index); + } + /** + * repeated string starring = 5; + * @param index The index to set the value at. + * @param value The starring to set. + * @return This builder for chaining. + */ + public Builder setStarring( + int index, java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + ensureStarringIsMutable(); + starring_.set(index, value); + onChanged(); + return this; + } + /** + * repeated string starring = 5; + * @param value The starring to add. + * @return This builder for chaining. + */ + public Builder addStarring( + java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + ensureStarringIsMutable(); + starring_.add(value); + onChanged(); + return this; + } + /** + * repeated string starring = 5; + * @param values The starring to add. + * @return This builder for chaining. + */ + public Builder addAllStarring( + java.lang.Iterable values) { + ensureStarringIsMutable(); + com.google.protobuf.AbstractMessageLite.Builder.addAll( + values, starring_); + onChanged(); + return this; + } + /** + * repeated string starring = 5; + * @return This builder for chaining. + */ + public Builder clearStarring() { + starring_ = com.google.protobuf.LazyStringArrayList.EMPTY; + bitField0_ = (bitField0_ & ~0x00000001); + onChanged(); + return this; + } + /** + * repeated string starring = 5; + * @param value The bytes of the starring to add. + * @return This builder for chaining. + */ + public Builder addStarringBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + checkByteStringIsUtf8(value); + ensureStarringIsMutable(); + starring_.add(value); + onChanged(); + return this; + } + @java.lang.Override + public final Builder setUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.setUnknownFields(unknownFields); + } + + @java.lang.Override + public final Builder mergeUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.mergeUnknownFields(unknownFields); + } + + + // @@protoc_insertion_point(builder_scope:org.akhq.utils.Film) + } + + // @@protoc_insertion_point(class_scope:org.akhq.utils.Film) + private static final org.akhq.utils.FilmProto.Film DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new org.akhq.utils.FilmProto.Film(); + } + + public static org.akhq.utils.FilmProto.Film getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser + PARSER = new com.google.protobuf.AbstractParser() { + @java.lang.Override + public Film parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new Film(input, extensionRegistry); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @java.lang.Override + public org.akhq.utils.FilmProto.Film getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + + } + + private static final com.google.protobuf.Descriptors.Descriptor + internal_static_org_akhq_utils_Film_descriptor; + private static final + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internal_static_org_akhq_utils_Film_fieldAccessorTable; + + public static com.google.protobuf.Descriptors.FileDescriptor + getDescriptor() { + return descriptor; + } + private static com.google.protobuf.Descriptors.FileDescriptor + descriptor; + static { + java.lang.String[] descriptorData = { + "\n\nfilm.proto\022\016org.akhq.utils\"`\n\004Film\022\014\n\004" + + "name\030\001 \001(\t\022\020\n\010producer\030\002 \001(\t\022\024\n\014release_" + + "year\030\003 \001(\005\022\020\n\010duration\030\004 \001(\005\022\020\n\010starring" + + "\030\005 \003(\tB\033\n\016org.akhq.utilsB\tFilmProtob\006pro" + + "to3" + }; + descriptor = com.google.protobuf.Descriptors.FileDescriptor + .internalBuildGeneratedFileFrom(descriptorData, + new com.google.protobuf.Descriptors.FileDescriptor[] { + }); + internal_static_org_akhq_utils_Film_descriptor = + getDescriptor().getMessageTypes().get(0); + internal_static_org_akhq_utils_Film_fieldAccessorTable = new + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( + internal_static_org_akhq_utils_Film_descriptor, + new java.lang.String[] { "Name", "Producer", "ReleaseYear", "Duration", "Starring", }); + } + + // @@protoc_insertion_point(outer_class_scope) +} diff --git a/src/test/java/org/akhq/utils/ProtobufToJsonDeserializerTest.java b/src/test/java/org/akhq/utils/ProtobufToJsonDeserializerTest.java new file mode 100644 index 000000000..75c025da7 --- /dev/null +++ b/src/test/java/org/akhq/utils/ProtobufToJsonDeserializerTest.java @@ -0,0 +1,131 @@ +package org.akhq.utils; + +import org.akhq.configs.Connection.ProtobufDeserializationTopicsMapping; +import org.akhq.configs.TopicsMapping; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.Base64; +import java.util.Collections; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +public class ProtobufToJsonDeserializerTest { + ProtobufDeserializationTopicsMapping protobufDeserializationTopicsMapping; + AlbumProto.Album albumProto; + FilmProto.Film filmProto; + + + @BeforeEach + public void before() throws URISyntaxException, IOException { + createTopicProtobufDeserializationMapping(); + createAlbumObject(); + createFilmObject(); + } + + private void createTopicProtobufDeserializationMapping() throws URISyntaxException, IOException { + protobufDeserializationTopicsMapping = new ProtobufDeserializationTopicsMapping(); + TopicsMapping albumTopicsMapping = new TopicsMapping(); + + albumTopicsMapping.setTopicRegex("album.*"); + String base64AlbumDescriptor = encodeDescriptorFileToBase64("album.desc"); + albumTopicsMapping.setDescriptorFileBase64(base64AlbumDescriptor); + albumTopicsMapping.setValueMessageType("Album"); + + TopicsMapping filmTopicsMapping = new TopicsMapping(); + filmTopicsMapping.setTopicRegex("film.*"); + String base64FilmDescriptor = encodeDescriptorFileToBase64("film.desc"); + filmTopicsMapping.setDescriptorFileBase64(base64FilmDescriptor); + filmTopicsMapping.setValueMessageType("Film"); + + protobufDeserializationTopicsMapping.setTopicsMapping(Arrays.asList(albumTopicsMapping, filmTopicsMapping)); + } + + private String encodeDescriptorFileToBase64(String descriptorFileName) throws URISyntaxException, IOException { + URI uri = ClassLoader.getSystemResource("protobuf_desc").toURI(); + String protobufDescriptorsFolder = Paths.get(uri).toString(); + + String fullName = protobufDescriptorsFolder + File.separator + descriptorFileName; + byte[] descriptorFileBytes = Files.readAllBytes(Path.of(fullName)); + return Base64.getEncoder().encodeToString(descriptorFileBytes); + } + + private void createAlbumObject() { + List artists = Collections.singletonList("Imagine Dragons"); + List songTitles = Arrays.asList("Birds", "Zero", "Natural", "Machine"); + Album album = new Album("Origins", artists, 2018, songTitles); + albumProto = AlbumProto.Album.newBuilder() + .setTitle(album.getTitle()) + .addAllArtist(album.getArtists()) + .setReleaseYear(album.getReleaseYear()) + .addAllSongTitle(album.getSongsTitles()) + .build(); + } + + private void createFilmObject() { + List starring = Arrays.asList("Harrison Ford", "Mark Hamill", "Carrie Fisher", "Adam Driver", "Daisy Ridley"); + Film film = new Film("Star Wars: The Force Awakens", "J. J. Abrams", 2015, 135, starring); + filmProto = FilmProto.Film.newBuilder() + .setName(film.getName()) + .setProducer(film.getProducer()) + .setReleaseYear(film.getReleaseYear()) + .setDuration(film.getDuration()) + .addAllStarring(film.getStarring()) + .build(); + } + + @Test + public void deserializeAlbum() { + ProtobufToJsonDeserializer protobufToJsonDeserializer = new ProtobufToJsonDeserializer(protobufDeserializationTopicsMapping); + final byte[] binaryAlbum = albumProto.toByteArray(); + String decodedAlbum = protobufToJsonDeserializer.deserialize("album.topic.name", binaryAlbum, false); + String expectedAlbum = "{\n" + + " \"title\": \"Origins\",\n" + + " \"artist\": [\"Imagine Dragons\"],\n" + + " \"releaseYear\": 2018,\n" + + " \"songTitle\": [\"Birds\", \"Zero\", \"Natural\", \"Machine\"]\n" + + "}"; + assertEquals(expectedAlbum, decodedAlbum); + } + + @Test + public void deserializeFilm() { + ProtobufToJsonDeserializer protobufToJsonDeserializer = new ProtobufToJsonDeserializer(protobufDeserializationTopicsMapping); + final byte[] binaryFilm = filmProto.toByteArray(); + String decodedFilm = protobufToJsonDeserializer.deserialize("film.topic.name", binaryFilm, false); + String expectedFilm = "{\n" + + " \"name\": \"Star Wars: The Force Awakens\",\n" + + " \"producer\": \"J. J. Abrams\",\n" + + " \"releaseYear\": 2015,\n" + + " \"duration\": 135,\n" + + " \"starring\": [\"Harrison Ford\", \"Mark Hamill\", \"Carrie Fisher\", \"Adam Driver\", \"Daisy Ridley\"]\n" + + "}"; + assertEquals(expectedFilm, decodedFilm); + } + + @Test + public void deserializeForNotMatchingTopic() { + ProtobufToJsonDeserializer protobufToJsonDeserializer = new ProtobufToJsonDeserializer(protobufDeserializationTopicsMapping); + final byte[] binaryFilm = filmProto.toByteArray(); + String decodedFilm = protobufToJsonDeserializer.deserialize("random.topic.name", binaryFilm, false); + assertNull(decodedFilm); + } + + @Test + public void deserializeForKeyWhenItsTypeNotSet() { + ProtobufToJsonDeserializer protobufToJsonDeserializer = new ProtobufToJsonDeserializer(protobufDeserializationTopicsMapping); + final byte[] binaryFilm = filmProto.toByteArray(); + String decodedFilm = protobufToJsonDeserializer.deserialize("film.topic.name", binaryFilm, true); + assertNull(decodedFilm); + } +} diff --git a/src/test/resources/protobuf_desc/album.desc b/src/test/resources/protobuf_desc/album.desc new file mode 100644 index 000000000..45e67e517 --- /dev/null +++ b/src/test/resources/protobuf_desc/album.desc @@ -0,0 +1,11 @@ + +� + album.protoorg.akhq.utils"w +Album +title ( Rtitle +artist ( Rartist! + release_year (R releaseYear + +song_title ( R songTitleB +org.akhq.utilsB +AlbumProtobproto3 \ No newline at end of file diff --git a/src/test/resources/protobuf_desc/film.desc b/src/test/resources/protobuf_desc/film.desc new file mode 100644 index 000000000..45a64d71f --- /dev/null +++ b/src/test/resources/protobuf_desc/film.desc @@ -0,0 +1,11 @@ + +� + +film.protoorg.akhq.utils"� +Film +name ( Rname +producer ( Rproducer! + release_year (R releaseYear +duration (Rduration +starring ( RstarringB +org.akhq.utilsB FilmProtobproto3 \ No newline at end of file diff --git a/src/test/resources/protobuf_proto/album.proto b/src/test/resources/protobuf_proto/album.proto new file mode 100644 index 000000000..f9fab8465 --- /dev/null +++ b/src/test/resources/protobuf_proto/album.proto @@ -0,0 +1,12 @@ +syntax = "proto3"; +package org.akhq.utils; + +option java_package = "org.akhq.utils"; +option java_outer_classname = "AlbumProto"; + +message Album { + string title = 1; + repeated string artist = 2; + int32 release_year = 3; + repeated string song_title = 4; +} \ No newline at end of file diff --git a/src/test/resources/protobuf_proto/film.proto b/src/test/resources/protobuf_proto/film.proto new file mode 100644 index 000000000..32632c039 --- /dev/null +++ b/src/test/resources/protobuf_proto/film.proto @@ -0,0 +1,13 @@ +syntax = "proto3"; +package org.akhq.utils; + +option java_package = "org.akhq.utils"; +option java_outer_classname = "FilmProto"; + +message Film { + string name = 1; + string producer = 2; + int32 release_year = 3; + int32 duration = 4; + repeated string starring = 5; +} \ No newline at end of file diff --git a/src/test/resources/protobuf_proto/generate-descriptor-file.sh b/src/test/resources/protobuf_proto/generate-descriptor-file.sh new file mode 100644 index 000000000..b0650a96e --- /dev/null +++ b/src/test/resources/protobuf_proto/generate-descriptor-file.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +# This script generates album.desc and film.desc files (binary protobuf descriptors) from album.proto and film.proto files + +protoc --descriptor_set_out="../protobuf_desc/film.desc" --include_imports film.proto \ No newline at end of file diff --git a/src/test/resources/protobuf_proto/generate-java-classes.sh b/src/test/resources/protobuf_proto/generate-java-classes.sh new file mode 100644 index 000000000..da9d43e26 --- /dev/null +++ b/src/test/resources/protobuf_proto/generate-java-classes.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +# This script generates AlbumProto and AlbumFilm Java classes from album.proto and film.proto files +# Run it every time when change album.proto and film.proto files + +SRC_DIR="." +DST_DIR="../../java" +protoc -I=${SRC_DIR} --java_out=${DST_DIR} ${SRC_DIR}/film.proto \ No newline at end of file