From 4567e4a4fbad21e4703db116459874c7c8dd61ac Mon Sep 17 00:00:00 2001 From: Michal Foksa Date: Sat, 7 May 2022 20:09:42 +0200 Subject: [PATCH] #310 New annotation @AvroNamespace to override Avro schema field namespace. Current namespace value is Java package name. This annotation allows to override its name. --- .../avro/annotations/AvroNamespace.java | 16 ++++ .../avro/schema/AvroSchemaHelper.java | 10 ++- .../avro/annotations/AvroNamespaceTest.java | 85 +++++++++++++++++++ 3 files changed, 107 insertions(+), 4 deletions(-) create mode 100644 avro/src/main/java/com/fasterxml/jackson/dataformat/avro/annotations/AvroNamespace.java create mode 100644 avro/src/test/java/com/fasterxml/jackson/dataformat/avro/annotations/AvroNamespaceTest.java diff --git a/avro/src/main/java/com/fasterxml/jackson/dataformat/avro/annotations/AvroNamespace.java b/avro/src/main/java/com/fasterxml/jackson/dataformat/avro/annotations/AvroNamespace.java new file mode 100644 index 000000000..f5e6959ad --- /dev/null +++ b/avro/src/main/java/com/fasterxml/jackson/dataformat/avro/annotations/AvroNamespace.java @@ -0,0 +1,16 @@ +package com.fasterxml.jackson.dataformat.avro.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotation allows to override default Avro type namespace value. + * Default value is Java package name. + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface AvroNamespace { + String value(); +} diff --git a/avro/src/main/java/com/fasterxml/jackson/dataformat/avro/schema/AvroSchemaHelper.java b/avro/src/main/java/com/fasterxml/jackson/dataformat/avro/schema/AvroSchemaHelper.java index 3dfba579f..8644edcde 100644 --- a/avro/src/main/java/com/fasterxml/jackson/dataformat/avro/schema/AvroSchemaHelper.java +++ b/avro/src/main/java/com/fasterxml/jackson/dataformat/avro/schema/AvroSchemaHelper.java @@ -8,6 +8,7 @@ import java.net.URL; import java.util.*; +import com.fasterxml.jackson.dataformat.avro.annotations.AvroNamespace; import org.apache.avro.JsonProperties; import org.apache.avro.Schema; import org.apache.avro.Schema.Parser; @@ -97,8 +98,9 @@ public static boolean isStringable(AnnotatedClass type) { return false; } - protected static String getNamespace(JavaType type) { - return getNamespace(type.getRawClass()); + protected static String getNamespace(BeanDescription bean) { + AvroNamespace ann = bean.getClassInfo().getAnnotation(AvroNamespace.class); + return ann != null ? ann.value() : getNamespace(bean.getType().getRawClass()); } protected static String getNamespace(Class cls) { @@ -244,7 +246,7 @@ public static Schema initializeRecordSchema(BeanDescription bean) { return addAlias(Schema.createRecord( getName(bean.getType()), bean.findClassDescription(), - getNamespace(bean.getType()), + getNamespace(bean), bean.getType().isTypeOrSubTypeOf(Throwable.class) ), bean); } @@ -268,7 +270,7 @@ public static Schema createEnumSchema(BeanDescription bean, List values) return addAlias(Schema.createEnum( getName(bean.getType()), bean.findClassDescription(), - getNamespace(bean.getType()), values + getNamespace(bean), values ), bean); } diff --git a/avro/src/test/java/com/fasterxml/jackson/dataformat/avro/annotations/AvroNamespaceTest.java b/avro/src/test/java/com/fasterxml/jackson/dataformat/avro/annotations/AvroNamespaceTest.java new file mode 100644 index 000000000..f24760420 --- /dev/null +++ b/avro/src/test/java/com/fasterxml/jackson/dataformat/avro/annotations/AvroNamespaceTest.java @@ -0,0 +1,85 @@ +package com.fasterxml.jackson.dataformat.avro.annotations; + +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.dataformat.avro.AvroMapper; +import com.fasterxml.jackson.dataformat.avro.schema.AvroSchemaGenerator; +import org.apache.avro.Schema; +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class AvroNamespaceTest { + + static class ClassWithoutAvroNamespaceAnnotation { + } + + @AvroNamespace("ClassWithAvroNamespaceAnnotation.namespace") + static class ClassWithAvroNamespaceAnnotation { + } + + enum EnumWithoutAvroNamespaceAnnotation {FOO, BAR;} + + @AvroNamespace("EnumWithAvroNamespaceAnnotation.namespace") + enum EnumWithAvroNamespaceAnnotation {FOO, BAR;} + + @Test + public void class_without_AvroNamespace_annotation_test() throws JsonMappingException { + // GIVEN + AvroMapper mapper = new AvroMapper(); + AvroSchemaGenerator gen = new AvroSchemaGenerator(); + + // WHEN + mapper.acceptJsonFormatVisitor(ClassWithoutAvroNamespaceAnnotation.class, gen); + Schema actualSchema = gen.getGeneratedSchema().getAvroSchema(); + + // THEN + assertThat(actualSchema.getNamespace()) + .isEqualTo("com.fasterxml.jackson.dataformat.avro.annotations.AvroNamespaceTest$"); + } + + @Test + public void class_with_AvroNamespace_annotation_test() throws JsonMappingException { + // GIVEN + AvroMapper mapper = new AvroMapper(); + AvroSchemaGenerator gen = new AvroSchemaGenerator(); + + // WHEN + mapper.acceptJsonFormatVisitor(ClassWithAvroNamespaceAnnotation.class, gen); + Schema actualSchema = gen.getGeneratedSchema().getAvroSchema(); + + // THEN + assertThat(actualSchema.getNamespace()) + .isEqualTo("ClassWithAvroNamespaceAnnotation.namespace"); + } + + @Test + public void enum_without_AvroNamespace_annotation_test() throws JsonMappingException { + // GIVEN + AvroMapper mapper = new AvroMapper(); + AvroSchemaGenerator gen = new AvroSchemaGenerator(); + + // WHEN + mapper.acceptJsonFormatVisitor(EnumWithoutAvroNamespaceAnnotation.class, gen); + Schema actualSchema = gen.getGeneratedSchema().getAvroSchema(); + + // THEN + assertThat(actualSchema.getNamespace()) + .isEqualTo("com.fasterxml.jackson.dataformat.avro.annotations.AvroNamespaceTest$"); + } + + @Test + public void enum_with_AvroNamespace_annotation_test() throws JsonMappingException { + // GIVEN + AvroMapper mapper = new AvroMapper(); + AvroSchemaGenerator gen = new AvroSchemaGenerator(); + + // WHEN + mapper.acceptJsonFormatVisitor(EnumWithAvroNamespaceAnnotation.class, gen); + Schema actualSchema = gen.getGeneratedSchema().getAvroSchema(); + + // THEN + assertThat(actualSchema.getNamespace()) + .isEqualTo("EnumWithAvroNamespaceAnnotation.namespace"); + } + +}