Skip to content

Commit 7e3c869

Browse files
authored
Update avro to 1.11.3 (backport to 2.18 branch) (#512)
* Update avro to 1.11.3 Namespace for nested classes no longer ends with '$'. This is how avro library generates schema since version 1.9. See: AVRO-2143 Please note that resolution of nested classes without '$' was implemented long ago in c570549. Fixes #167 * Remove '$' from Avro namespaces when Java class has multiple nesting levels Avro before 1.11 was generating schemas with '$' in namespace if class had multiple nesting levels. To fix compatibility with avro 1.11+ make sure all dollar characters are replaced by dots. Related: AVRO-2757
1 parent 9e27d43 commit 7e3c869

File tree

6 files changed

+74
-13
lines changed

6 files changed

+74
-13
lines changed

avro/pom.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ abstractions.
4747
<dependency>
4848
<groupId>org.apache.avro</groupId>
4949
<artifactId>avro</artifactId>
50-
<version>1.8.2</version>
50+
<version>1.11.3</version>
5151
</dependency>
5252

5353
<!-- and for testing we need logback -->

avro/src/main/java/com/fasterxml/jackson/dataformat/avro/schema/AvroSchemaHelper.java

+24-8
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,9 @@ protected static String getNamespace(Class<?> cls) {
108108
// NOTE: was reverted in 2.8.8, but is enabled for Jackson 2.9.
109109
Class<?> enclosing = cls.getEnclosingClass();
110110
if (enclosing != null) {
111-
return enclosing.getName() + "$";
111+
// 23-Aug-2024: Changed as per [dataformats-binary#167]
112+
// Enclosing class may also be nested
113+
return enclosing.getName().replace('$', '.');
112114
}
113115
Package pkg = cls.getPackage();
114116
return (pkg == null) ? "" : pkg.getName();
@@ -351,6 +353,8 @@ public static String getFullName(Schema schema) {
351353
if (namespace == null) {
352354
return name;
353355
}
356+
// 23-Aug-2024: [dataformats-binary#167] Still needed for backwards-compatibility
357+
// with schemas that use dollar sign for nested classes (Apache Avro before 1.9)
354358
final int len = namespace.length();
355359
if (namespace.charAt(len-1) == '$') {
356360
return namespace + name;
@@ -441,13 +445,25 @@ private static String _resolve(FullNameKey key) {
441445
// Check if this is a nested class
442446
// 19-Sep-2020, tatu: This is a horrible, horribly inefficient and all-around
443447
// wrong mechanism. To be abolished if possible.
444-
final String nestedClassName = key.nameWithSeparator('$');
445-
try {
446-
Class.forName(nestedClassName);
447-
return nestedClassName;
448-
} catch (ClassNotFoundException e) {
449-
// Could not find a nested class, must be a regular class
450-
return key.nameWithSeparator('.');
448+
// 23-Aug-2024:[dataformats-binary#167] Based on SpecificData::getClass
449+
// from Apache Avro. Initially assume that namespace is a Java package
450+
StringBuilder sb = new StringBuilder(key.nameWithSeparator('.'));
451+
int lastDot = sb.length();
452+
while (true) {
453+
try {
454+
// Try to resolve the class
455+
String className = sb.toString();
456+
Class.forName(className);
457+
return className;
458+
} catch (ClassNotFoundException e) {
459+
// Class does not exist - perhaps last dot is actually a nested class
460+
lastDot = sb.lastIndexOf(".", lastDot);
461+
if (lastDot == -1) {
462+
// No more dots so we are unable to resolve, should we throw an exception?
463+
return key.nameWithSeparator('.');
464+
}
465+
sb.setCharAt(lastDot, '$');
466+
}
451467
}
452468
}
453469
}

avro/src/test/java/com/fasterxml/jackson/dataformat/avro/annotation/AvroNamespaceTest.java

+40-2
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,15 @@ enum EnumWithoutAvroNamespaceAnnotation {FOO, BAR;}
2323
@AvroNamespace("EnumWithAvroNamespaceAnnotation.namespace")
2424
enum EnumWithAvroNamespaceAnnotation {FOO, BAR;}
2525

26+
static class Foo {
27+
static class Bar {
28+
static class ClassWithMultipleNestingLevels {
29+
}
30+
31+
enum EnumWithMultipleNestingLevels {FOO, BAR;}
32+
}
33+
}
34+
2635
@Test
2736
public void class_without_AvroNamespace_test() throws Exception {
2837
// GIVEN
@@ -35,7 +44,7 @@ public void class_without_AvroNamespace_test() throws Exception {
3544

3645
// THEN
3746
assertThat(actualSchema.getNamespace())
38-
.isEqualTo("com.fasterxml.jackson.dataformat.avro.annotation.AvroNamespaceTest$");
47+
.isEqualTo("com.fasterxml.jackson.dataformat.avro.annotation.AvroNamespaceTest");
3948
}
4049

4150
@Test
@@ -53,6 +62,21 @@ public void class_with_AvroNamespace_test() throws Exception {
5362
.isEqualTo("ClassWithAvroNamespaceAnnotation.namespace");
5463
}
5564

65+
@Test
66+
public void class_with_multiple_nesting_levels_test() throws Exception {
67+
// GIVEN
68+
AvroMapper mapper = new AvroMapper();
69+
AvroSchemaGenerator gen = new AvroSchemaGenerator();
70+
71+
// WHEN
72+
mapper.acceptJsonFormatVisitor(Foo.Bar.ClassWithMultipleNestingLevels.class, gen);
73+
Schema actualSchema = gen.getGeneratedSchema().getAvroSchema();
74+
75+
// THEN
76+
assertThat(actualSchema.getNamespace())
77+
.isEqualTo("com.fasterxml.jackson.dataformat.avro.annotation.AvroNamespaceTest.Foo.Bar");
78+
}
79+
5680
@Test
5781
public void enum_without_AvroNamespace_test() throws Exception {
5882
// GIVEN
@@ -65,7 +89,7 @@ public void enum_without_AvroNamespace_test() throws Exception {
6589

6690
// THEN
6791
assertThat(actualSchema.getNamespace())
68-
.isEqualTo("com.fasterxml.jackson.dataformat.avro.annotation.AvroNamespaceTest$");
92+
.isEqualTo("com.fasterxml.jackson.dataformat.avro.annotation.AvroNamespaceTest");
6993
}
7094

7195
@Test
@@ -83,4 +107,18 @@ public void enum_with_AvroNamespace_test() throws Exception {
83107
.isEqualTo("EnumWithAvroNamespaceAnnotation.namespace");
84108
}
85109

110+
@Test
111+
public void enum_with_multiple_nesting_levels_test() throws Exception {
112+
// GIVEN
113+
AvroMapper mapper = new AvroMapper();
114+
AvroSchemaGenerator gen = new AvroSchemaGenerator();
115+
116+
// WHEN
117+
mapper.acceptJsonFormatVisitor(Foo.Bar.EnumWithMultipleNestingLevels.class, gen);
118+
Schema actualSchema = gen.getGeneratedSchema().getAvroSchema();
119+
120+
// THEN
121+
assertThat(actualSchema.getNamespace())
122+
.isEqualTo("com.fasterxml.jackson.dataformat.avro.annotation.AvroNamespaceTest.Foo.Bar");
123+
}
86124
}

avro/src/test/java/com/fasterxml/jackson/dataformat/avro/interop/annotations/AvroAliasTest.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616
public class AvroAliasTest extends InteropTestBase {
1717

18-
@AvroAlias(alias = "Employee", space = "com.fasterxml.jackson.dataformat.avro.AvroTestBase$")
18+
@AvroAlias(alias = "Employee", space = "com.fasterxml.jackson.dataformat.avro.AvroTestBase")
1919
public static class NewEmployee {
2020

2121
public String name;
@@ -40,7 +40,7 @@ public static class AliasedNameEmployee {
4040
public AliasedNameEmployee boss;
4141
}
4242

43-
@AvroAlias(alias = "Size", space = "com.fasterxml.jackson.dataformat.avro.AvroTestBase$")
43+
@AvroAlias(alias = "Size", space = "com.fasterxml.jackson.dataformat.avro.AvroTestBase")
4444
public enum NewSize {
4545
SMALL,
4646
LARGE;

release-notes/CREDITS-2.x

+4
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,10 @@ Yoann Vernageau (@yvrng)
328328
when source is an empty `InputStream`
329329
(2.17.1)
330330

331+
Rafał Harabień (@rafalh)
332+
* Contributed fix for #167: (avro) Incompatibility with Avro >=1.9.0 (upgrade to Avro 1.11.3)
333+
(2.18.0)
334+
331335
PJ Fanning (@pjfanning)
332336
* Contributed #484: (protobuf) Rework synchronization in `ProtobufMapper`
333337
(2.18.0)

release-notes/VERSION-2.x

+3
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ Active maintainers:
1616

1717
2.18.0 (not yet released)
1818

19+
#167: (avro) Incompatibility with Avro >=1.9.0 (upgrade to Avro 1.11.3)
20+
(reported by @Sage-Pierce)
21+
(fix contributed by Rafał H)
1922
#484: (protobuf) Rework synchronization in `ProtobufMapper`
2023
(contributed by @pjfanning)
2124
#494: (avro) Avro Schema generation: allow mapping Java Enum properties to

0 commit comments

Comments
 (0)