This is an automated email from the ASF dual-hosted git repository. jamesnetherton pushed a commit to branch 3.15.x in repository https://gitbox.apache.org/repos/asf/camel-quarkus.git
commit 3ebbabcf94e252acd911fe78c780160492576d7b Author: James Netherton <jamesnether...@gmail.com> AuthorDate: Wed Oct 30 15:59:09 2024 +0000 Work around issues for avro-jackson compatibility with Avro 1.12.x Fixes #6721 --- .../avro/deployment/JacksonAvroProcessor.java | 50 ++++++++++++++++++++++ .../jackson/avro/it/JacksonAvroResource.java | 36 ++++++++++++++++ .../component/jackson/avro/it/JacksonAvroTest.java | 13 ++++++ 3 files changed, 99 insertions(+) diff --git a/extensions/jackson-avro/deployment/src/main/java/org/apache/camel/quarkus/component/jackson/avro/deployment/JacksonAvroProcessor.java b/extensions/jackson-avro/deployment/src/main/java/org/apache/camel/quarkus/component/jackson/avro/deployment/JacksonAvroProcessor.java index 9d9923e786..86e71872ab 100644 --- a/extensions/jackson-avro/deployment/src/main/java/org/apache/camel/quarkus/component/jackson/avro/deployment/JacksonAvroProcessor.java +++ b/extensions/jackson-avro/deployment/src/main/java/org/apache/camel/quarkus/component/jackson/avro/deployment/JacksonAvroProcessor.java @@ -16,10 +16,18 @@ */ package org.apache.camel.quarkus.component.jackson.avro.deployment; +import java.util.function.BooleanSupplier; + import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.builditem.BytecodeTransformerBuildItem; import io.quarkus.deployment.builditem.FeatureBuildItem; import io.quarkus.deployment.builditem.nativeimage.RuntimeInitializedClassBuildItem; +import io.quarkus.gizmo.Gizmo; +import org.apache.avro.Schema.Parser; import org.apache.avro.file.DataFileWriter; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; class JacksonAvroProcessor { private static final String FEATURE = "camel-jackson-avro"; @@ -33,4 +41,46 @@ class JacksonAvroProcessor { RuntimeInitializedClassBuildItem runtimeInitializedClass() { return new RuntimeInitializedClassBuildItem(DataFileWriter.class.getName()); } + + @BuildStep(onlyIfNot = { AvroParserSetValidateMethodPresent.class }) + BytecodeTransformerBuildItem patchAvroJacksonCompatibility() { + // Hack to maintain avro-jackson compatibility with Avro 1.12.x + // Adds a noop version of Parser.setValidate that got removed in Avro 1.12.x. + // public Parser setValidate(boolean validate) { + // return this; + // } + return new BytecodeTransformerBuildItem.Builder() + .setClassToTransform(Parser.class.getName()) + .setCacheable(true) + .setVisitorFunction((className, classVisitor) -> { + return new ClassVisitor(Gizmo.ASM_API_VERSION, classVisitor) { + @Override + public void visitEnd() { + MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC, "setValidate", + "(Z)Lorg/apache/avro/Schema$Parser;", null, null); + if (mv != null) { + mv.visitCode(); + mv.visitVarInsn(Opcodes.ALOAD, 0); + mv.visitInsn(Opcodes.ARETURN); + mv.visitMaxs(1, 2); + mv.visitEnd(); + } + super.visitEnd(); + } + }; + }) + .build(); + } + + static class AvroParserSetValidateMethodPresent implements BooleanSupplier { + @Override + public boolean getAsBoolean() { + try { + Parser.class.getDeclaredMethod("setValidate", boolean.class); + return true; + } catch (NoSuchMethodException e) { + return false; + } + } + } } diff --git a/integration-tests/jackson-avro/src/main/java/org/apache/camel/quarkus/component/jackson/avro/it/JacksonAvroResource.java b/integration-tests/jackson-avro/src/main/java/org/apache/camel/quarkus/component/jackson/avro/it/JacksonAvroResource.java index 85da1b0e43..3272edc07a 100644 --- a/integration-tests/jackson-avro/src/main/java/org/apache/camel/quarkus/component/jackson/avro/it/JacksonAvroResource.java +++ b/integration-tests/jackson-avro/src/main/java/org/apache/camel/quarkus/component/jackson/avro/it/JacksonAvroResource.java @@ -18,6 +18,9 @@ package org.apache.camel.quarkus.component.jackson.avro.it; import java.io.IOException; import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; import java.util.StringJoiner; @@ -30,14 +33,19 @@ import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; import jakarta.inject.Named; import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.GET; import jakarta.ws.rs.POST; import jakarta.ws.rs.Path; import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.QueryParam; +import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; import org.apache.avro.Schema; import org.apache.camel.ProducerTemplate; import org.apache.camel.component.jackson.SchemaResolver; import org.apache.camel.component.jackson.avro.JacksonAvroDataFormat; +import org.apache.commons.io.IOUtils; @Path("/jackson-avro") @ApplicationScoped @@ -93,6 +101,34 @@ public class JacksonAvroResource { return builder.build(); } + @Path("/custom/mapper") + @GET + @Produces(MediaType.TEXT_PLAIN) + public String customMapper(@QueryParam("schemaFrom") String schemaFrom) throws IOException { + AvroMapper avroMapper = new AvroMapper(); + AvroSchema avroSchema; + + if (schemaFrom.equals("classpath")) { + avroSchema = avroMapper.schemaFrom(JacksonAvroResource.class.getResourceAsStream("/pojo.avsc")); + } else if (schemaFrom.equals("string")) { + try (InputStream stream = JacksonAvroResource.class.getResourceAsStream("/pojo.avsc")) { + avroSchema = avroMapper.schemaFrom(IOUtils.toString(stream, StandardCharsets.UTF_8)); + } + } else if (schemaFrom.equals("file")) { + java.nio.file.Path schemaFile = Paths.get("target/schema.avsc"); + try (InputStream stream = JacksonAvroResource.class.getResourceAsStream("/pojo.avsc")) { + Files.write(schemaFile, stream.readAllBytes()); + avroSchema = avroMapper.schemaFrom(schemaFile.toFile()); + } finally { + Files.deleteIfExists(schemaFile); + } + } else { + throw new IllegalArgumentException("Unknown schema from option: " + schemaFrom); + } + + return avroSchema.getAvroSchema().getName(); + } + @Named public SchemaResolver avroSchemaResolver() throws IOException { return createSchemaResolver("/pojo.avsc"); diff --git a/integration-tests/jackson-avro/src/test/java/org/apache/camel/quarkus/component/jackson/avro/it/JacksonAvroTest.java b/integration-tests/jackson-avro/src/test/java/org/apache/camel/quarkus/component/jackson/avro/it/JacksonAvroTest.java index c00c2e6072..0b11cdc83a 100644 --- a/integration-tests/jackson-avro/src/test/java/org/apache/camel/quarkus/component/jackson/avro/it/JacksonAvroTest.java +++ b/integration-tests/jackson-avro/src/test/java/org/apache/camel/quarkus/component/jackson/avro/it/JacksonAvroTest.java @@ -19,6 +19,8 @@ package org.apache.camel.quarkus.component.jackson.avro.it; import io.quarkus.test.junit.QuarkusTest; import io.restassured.RestAssured; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import static org.apache.camel.quarkus.component.jackson.avro.it.StringAppendingDeserializer.STRING_TO_APPEND; import static org.hamcrest.Matchers.equalTo; @@ -87,4 +89,15 @@ class JacksonAvroTest { .statusCode(200) .body(equalTo(message)); } + + @ParameterizedTest + @ValueSource(strings = { "classpath", "file", "string" }) + public void customAvroMapper(String schemaFrom) { + RestAssured.given() + .queryParam("schemaFrom", schemaFrom) + .get("/jackson-avro/custom/mapper") + .then() + .statusCode(200) + .body(equalTo("Pojo")); + } }