This is an automated email from the ASF dual-hosted git repository.
fmariani pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/main by this push:
new 0a2935765612 Handle java.time* type conversion in Avro Dataformat
0a2935765612 is described below
commit 0a293576561223ad7f01e8e69ce9ee852edda30c
Author: Croway <[email protected]>
AuthorDate: Tue Oct 28 18:58:40 2025 +0100
Handle java.time* type conversion in Avro Dataformat
---
.../camel/dataformat/avro/AvroDataFormat.java | 21 +-
.../camel/dataformat/avro/SpecificDataNoCache.java | 22 +-
.../avro/AvroDateMarshalAndUnmarshalTest.java | 112 ++++++
.../AvroDateRecordMarshalAndUnmarshalTest.java | 147 +++++++
.../camel/dataformat/avro/example/DateRecord.java | 428 +++++++++++++++++++++
.../org/apache/camel/dataformat/avro/date.avsc | 29 ++
6 files changed, 751 insertions(+), 8 deletions(-)
diff --git
a/components/camel-avro/src/main/java/org/apache/camel/dataformat/avro/AvroDataFormat.java
b/components/camel-avro/src/main/java/org/apache/camel/dataformat/avro/AvroDataFormat.java
index 944235ca3244..ebded0dcd21f 100644
---
a/components/camel-avro/src/main/java/org/apache/camel/dataformat/avro/AvroDataFormat.java
+++
b/components/camel-avro/src/main/java/org/apache/camel/dataformat/avro/AvroDataFormat.java
@@ -138,7 +138,9 @@ public class AvroDataFormat extends ServiceSupport
implements DataFormat, DataFo
// the schema should be from the graph class name
Schema useSchema = actualSchema != null ? actualSchema :
loadSchema(graph.getClass().getName());
- DatumWriter<Object> datum = new SpecificDatumWriter<>(useSchema);
+ SpecificData specificData = getSpecificData(useSchema);
+
+ DatumWriter<Object> datum = new SpecificDatumWriter<>(useSchema,
specificData);
Encoder encoder = EncoderFactory.get().binaryEncoder(outputStream,
null);
datum.write(graph, encoder);
encoder.flush();
@@ -148,17 +150,22 @@ public class AvroDataFormat extends ServiceSupport
implements DataFormat, DataFo
public Object unmarshal(Exchange exchange, InputStream inputStream) throws
Exception {
ObjectHelper.notNull(actualSchema, "schema", this);
- ClassLoader classLoader = null;
- Class<?> clazz =
camelContext.getClassResolver().resolveClass(actualSchema.getFullName());
+ SpecificData specificData = getSpecificData(actualSchema);
+ DatumReader<GenericRecord> reader = new SpecificDatumReader<>(null,
null, specificData);
+ reader.setSchema(actualSchema);
+ Decoder decoder = DecoderFactory.get().binaryDecoder(inputStream,
null);
+ return reader.read(null, decoder);
+ }
+ private SpecificData getSpecificData(Schema schema) {
+ // Use SpecificDataNoCache to support logical type conversions (date,
timestamp, UUID, decimal, etc.)
+ ClassLoader classLoader = null;
+ Class<?> clazz =
camelContext.getClassResolver().resolveClass(schema.getFullName());
if (clazz != null) {
classLoader = clazz.getClassLoader();
}
SpecificData specificData = new SpecificDataNoCache(classLoader);
- DatumReader<GenericRecord> reader = new SpecificDatumReader<>(null,
null, specificData);
- reader.setSchema(actualSchema);
- Decoder decoder = DecoderFactory.get().binaryDecoder(inputStream,
null);
- return reader.read(null, decoder);
+ return specificData;
}
}
diff --git
a/components/camel-avro/src/main/java/org/apache/camel/dataformat/avro/SpecificDataNoCache.java
b/components/camel-avro/src/main/java/org/apache/camel/dataformat/avro/SpecificDataNoCache.java
index 92a3d92312f6..30748226353e 100644
---
a/components/camel-avro/src/main/java/org/apache/camel/dataformat/avro/SpecificDataNoCache.java
+++
b/components/camel-avro/src/main/java/org/apache/camel/dataformat/avro/SpecificDataNoCache.java
@@ -17,20 +17,40 @@
package org.apache.camel.dataformat.avro;
import org.apache.avro.Schema;
+import org.apache.avro.data.TimeConversions;
import org.apache.avro.specific.SpecificData;
public class SpecificDataNoCache extends SpecificData {
public SpecificDataNoCache() {
+ super();
+ addLogicalTypeConversions();
}
public SpecificDataNoCache(ClassLoader classLoader) {
super(classLoader);
+ addLogicalTypeConversions();
+ }
+
+ private void addLogicalTypeConversions() {
+ addLogicalTypeConversion(new TimeConversions.DateConversion());
+ addLogicalTypeConversion(new TimeConversions.TimeMillisConversion());
+ addLogicalTypeConversion(new TimeConversions.TimeMicrosConversion());
+ addLogicalTypeConversion(new
TimeConversions.TimestampMillisConversion());
+ addLogicalTypeConversion(new
TimeConversions.TimestampMicrosConversion());
+ addLogicalTypeConversion(new
TimeConversions.TimestampNanosConversion());
+ addLogicalTypeConversion(new
TimeConversions.LocalTimestampMillisConversion());
+ addLogicalTypeConversion(new
TimeConversions.LocalTimestampMicrosConversion());
+ addLogicalTypeConversion(new
TimeConversions.LocalTimestampNanosConversion());
+
+ addLogicalTypeConversion(new
org.apache.avro.Conversions.UUIDConversion());
+
+ addLogicalTypeConversion(new
org.apache.avro.Conversions.DecimalConversion());
}
@Override
public Object newRecord(Object old, Schema schema) {
- Class c = new SpecificDataNoCache().getClass(schema);
+ Class<?> c = this.getClass(schema);
return c == null ? super.newRecord(old, schema) : (c.isInstance(old) ?
old : newInstance(c, schema));
}
diff --git
a/components/camel-avro/src/test/java/org/apache/camel/dataformat/avro/AvroDateMarshalAndUnmarshalTest.java
b/components/camel-avro/src/test/java/org/apache/camel/dataformat/avro/AvroDateMarshalAndUnmarshalTest.java
new file mode 100644
index 000000000000..626553ba9020
--- /dev/null
+++
b/components/camel-avro/src/test/java/org/apache/camel/dataformat/avro/AvroDateMarshalAndUnmarshalTest.java
@@ -0,0 +1,112 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.dataformat.avro;
+
+import java.io.File;
+import java.io.IOException;
+import java.time.Instant;
+import java.time.LocalDate;
+
+import org.apache.avro.Schema;
+import org.apache.avro.generic.GenericData;
+import org.apache.avro.generic.GenericRecord;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class AvroDateMarshalAndUnmarshalTest extends CamelTestSupport {
+
+ private Schema schema;
+
+ @Override
+ public void doPreSetup() throws Exception {
+ schema = getSchema();
+ }
+
+ @Test
+ public void testDateMarshalAndUnmarshal() throws InterruptedException {
+ marshalAndUnmarshalDate("direct:in", "direct:back");
+ }
+
+ @Test
+ public void testDateWithNullValues() throws InterruptedException {
+ GenericRecord input = new GenericData.Record(schema);
+ input.put("date", null);
+ input.put("timestamp", null);
+
+ MockEndpoint mock = getMockEndpoint("mock:reverse");
+ mock.expectedMessageCount(1);
+ mock.message(0).body().isInstanceOf(GenericRecord.class);
+
+ Object marshalled = template.requestBody("direct:in", input);
+ template.sendBody("direct:back", marshalled);
+ mock.assertIsSatisfied();
+
+ GenericRecord output =
mock.getReceivedExchanges().get(0).getIn().getBody(GenericRecord.class);
+ assertEquals(input.get("date"), output.get("date"));
+ assertEquals(input.get("timestamp"), output.get("timestamp"));
+ }
+
+ private void marshalAndUnmarshalDate(String inURI, String outURI) throws
InterruptedException {
+ GenericRecord input = new GenericData.Record(schema);
+
+ // Avro date logical type - use LocalDate directly
+ LocalDate date = LocalDate.of(2025, 10, 28);
+ input.put("date", date);
+
+ // Avro timestamp-millis logical type - use Instant directly
+ Instant timestamp = Instant.ofEpochMilli(1730000000000L); // Fixed
timestamp for consistent testing
+ input.put("timestamp", timestamp);
+
+ MockEndpoint mock = getMockEndpoint("mock:reverse");
+ mock.expectedMessageCount(1);
+ mock.message(0).body().isInstanceOf(GenericRecord.class);
+
+ Object marshalled = template.requestBody(inURI, input);
+ template.sendBody(outURI, marshalled);
+ mock.assertIsSatisfied();
+
+ GenericRecord output =
mock.getReceivedExchanges().get(0).getIn().getBody(GenericRecord.class);
+ // The logical type conversions ensure date and timestamp are
converted back to LocalDate and Instant
+ assertEquals(date, output.get("date"));
+ assertEquals(timestamp, output.get("timestamp"));
+ }
+
+ @Override
+ protected RouteBuilder createRouteBuilder() {
+ return new RouteBuilder() {
+ @Override
+ public void configure() {
+ AvroDataFormat format = new AvroDataFormat(schema);
+
+ from("direct:in").marshal(format);
+ from("direct:back").unmarshal(format).to("mock:reverse");
+ }
+ };
+ }
+
+ private Schema getSchema() throws IOException {
+ String schemaLocation = getClass().getResource("date.avsc").getFile();
+ File schemaFile = new File(schemaLocation);
+ assertTrue(schemaFile.exists());
+ return new Schema.Parser().parse(schemaFile);
+ }
+}
diff --git
a/components/camel-avro/src/test/java/org/apache/camel/dataformat/avro/AvroDateRecordMarshalAndUnmarshalTest.java
b/components/camel-avro/src/test/java/org/apache/camel/dataformat/avro/AvroDateRecordMarshalAndUnmarshalTest.java
new file mode 100644
index 000000000000..3ce376faedd9
--- /dev/null
+++
b/components/camel-avro/src/test/java/org/apache/camel/dataformat/avro/AvroDateRecordMarshalAndUnmarshalTest.java
@@ -0,0 +1,147 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.dataformat.avro;
+
+import java.time.Instant;
+import java.time.LocalDate;
+
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.dataformat.avro.example.DateRecord;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+public class AvroDateRecordMarshalAndUnmarshalTest extends CamelTestSupport {
+
+ @Test
+ public void testDateRecordMarshalAndUnmarshal() throws
InterruptedException {
+ LocalDate date = LocalDate.of(2025, 10, 28);
+ Instant timestamp = Instant.parse("2024-01-15T10:30:00Z");
+
+ DateRecord input = new DateRecord(date, timestamp);
+
+ MockEndpoint mock = getMockEndpoint("mock:reverse");
+ mock.expectedMessageCount(1);
+ mock.message(0).body().isInstanceOf(DateRecord.class);
+
+ Object marshalled = template.requestBody("direct:in", input);
+ template.sendBody("direct:back", marshalled);
+ mock.assertIsSatisfied();
+
+ DateRecord output =
mock.getReceivedExchanges().get(0).getIn().getBody(DateRecord.class);
+ assertNotNull(output);
+ assertEquals(date, output.getDate());
+ assertEquals(timestamp, output.getTimestamp());
+ }
+
+ @Test
+ public void testDateRecordWithNullValues() throws InterruptedException {
+ DateRecord input = new DateRecord(null, null);
+
+ MockEndpoint mock = getMockEndpoint("mock:reverse");
+ mock.expectedMessageCount(1);
+ mock.message(0).body().isInstanceOf(DateRecord.class);
+
+ Object marshalled = template.requestBody("direct:in", input);
+ template.sendBody("direct:back", marshalled);
+ mock.assertIsSatisfied();
+
+ DateRecord output =
mock.getReceivedExchanges().get(0).getIn().getBody(DateRecord.class);
+ assertNotNull(output);
+ assertNull(output.getDate());
+ assertNull(output.getTimestamp());
+ }
+
+ @Test
+ public void testDateRecordWithBuilder() throws InterruptedException {
+ LocalDate date = LocalDate.of(2024, 1, 15);
+ Instant timestamp = Instant.parse("2024-01-15T10:30:00Z");
+
+ DateRecord input = DateRecord.newBuilder()
+ .setDate(date)
+ .setTimestamp(timestamp)
+ .build();
+
+ MockEndpoint mock = getMockEndpoint("mock:reverse");
+ mock.expectedMessageCount(1);
+ mock.message(0).body().isInstanceOf(DateRecord.class);
+
+ Object marshalled = template.requestBody("direct:in", input);
+ template.sendBody("direct:back", marshalled);
+ mock.assertIsSatisfied();
+
+ DateRecord output =
mock.getReceivedExchanges().get(0).getIn().getBody(DateRecord.class);
+ assertNotNull(output);
+ assertEquals(date, output.getDate());
+ assertEquals(timestamp, output.getTimestamp());
+ }
+
+ @Test
+ public void testDateRecordWithOnlyDate() throws InterruptedException {
+ LocalDate date = LocalDate.of(2025, 12, 25);
+ DateRecord input = new DateRecord(date, null);
+
+ MockEndpoint mock = getMockEndpoint("mock:reverse");
+ mock.expectedMessageCount(1);
+ mock.message(0).body().isInstanceOf(DateRecord.class);
+
+ Object marshalled = template.requestBody("direct:in", input);
+ template.sendBody("direct:back", marshalled);
+ mock.assertIsSatisfied();
+
+ DateRecord output =
mock.getReceivedExchanges().get(0).getIn().getBody(DateRecord.class);
+ assertNotNull(output);
+ assertEquals(date, output.getDate());
+ assertNull(output.getTimestamp());
+ }
+
+ @Test
+ public void testDateRecordWithOnlyTimestamp() throws InterruptedException {
+ Instant timestamp = Instant.parse("2024-01-15T10:30:00Z");
+ DateRecord input = new DateRecord(null, timestamp);
+
+ MockEndpoint mock = getMockEndpoint("mock:reverse");
+ mock.expectedMessageCount(1);
+ mock.message(0).body().isInstanceOf(DateRecord.class);
+
+ Object marshalled = template.requestBody("direct:in", input);
+ template.sendBody("direct:back", marshalled);
+ mock.assertIsSatisfied();
+
+ DateRecord output =
mock.getReceivedExchanges().get(0).getIn().getBody(DateRecord.class);
+ assertNotNull(output);
+ assertNull(output.getDate());
+ assertEquals(timestamp, output.getTimestamp());
+ }
+
+ @Override
+ protected RouteBuilder createRouteBuilder() {
+ return new RouteBuilder() {
+ @Override
+ public void configure() {
+ AvroDataFormat format = new AvroDataFormat(DateRecord.SCHEMA$);
+
+ from("direct:in").marshal(format);
+ from("direct:back").unmarshal(format).to("mock:reverse");
+ }
+ };
+ }
+}
diff --git
a/components/camel-avro/src/test/java/org/apache/camel/dataformat/avro/example/DateRecord.java
b/components/camel-avro/src/test/java/org/apache/camel/dataformat/avro/example/DateRecord.java
new file mode 100644
index 000000000000..5aa631103dee
--- /dev/null
+++
b/components/camel-avro/src/test/java/org/apache/camel/dataformat/avro/example/DateRecord.java
@@ -0,0 +1,428 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Autogenerated by Avro
+ *
+ * DO NOT EDIT DIRECTLY
+ */
+package org.apache.camel.dataformat.avro.example;
+
+import org.apache.avro.message.BinaryMessageDecoder;
+import org.apache.avro.message.BinaryMessageEncoder;
+import org.apache.avro.message.SchemaStore;
+import org.apache.avro.specific.SpecificData;
+
[email protected]
+public class DateRecord extends org.apache.avro.specific.SpecificRecordBase
implements org.apache.avro.specific.SpecificRecord {
+ private static final long serialVersionUID = 8653491882695437570L;
+
+ public static final org.apache.avro.Schema SCHEMA$ = new
org.apache.avro.Schema.Parser().parse(
+
"{\"type\":\"record\",\"name\":\"DateRecord\",\"namespace\":\"org.apache.camel.dataformat.avro.example\",\"fields\":[{\"name\":\"date\",\"type\":[\"null\",{\"type\":\"int\",\"logicalType\":\"date\"}],\"default\":null},{\"name\":\"timestamp\",\"type\":[\"null\",{\"type\":\"long\",\"logicalType\":\"timestamp-millis\"}],\"default\":null}]}");
+
+ public static org.apache.avro.Schema getClassSchema() {
+ return SCHEMA$;
+ }
+
+ private static final SpecificData MODEL$ = new SpecificData();
+ static {
+ MODEL$.addLogicalTypeConversion(new
org.apache.avro.data.TimeConversions.DateConversion());
+ MODEL$.addLogicalTypeConversion(new
org.apache.avro.data.TimeConversions.TimestampMillisConversion());
+ }
+
+ private static final BinaryMessageEncoder<DateRecord> ENCODER = new
BinaryMessageEncoder<>(MODEL$, SCHEMA$);
+
+ private static final BinaryMessageDecoder<DateRecord> DECODER = new
BinaryMessageDecoder<>(MODEL$, SCHEMA$);
+
+ /**
+ * Return the BinaryMessageEncoder instance used by this class.
+ *
+ * @return the message encoder used by this class
+ */
+ public static BinaryMessageEncoder<DateRecord> getEncoder() {
+ return ENCODER;
+ }
+
+ /**
+ * Return the BinaryMessageDecoder instance used by this class.
+ *
+ * @return the message decoder used by this class
+ */
+ public static BinaryMessageDecoder<DateRecord> getDecoder() {
+ return DECODER;
+ }
+
+ /**
+ * Create a new BinaryMessageDecoder instance for this class that uses the
specified {@link SchemaStore}.
+ *
+ * @param resolver a {@link SchemaStore} used to find schemas by
fingerprint
+ * @return a BinaryMessageDecoder instance for this class backed
by the given SchemaStore
+ */
+ public static BinaryMessageDecoder<DateRecord> createDecoder(SchemaStore
resolver) {
+ return new BinaryMessageDecoder<>(MODEL$, SCHEMA$, resolver);
+ }
+
+ /**
+ * Serializes this DateRecord to a ByteBuffer.
+ *
+ * @return a buffer holding the serialized data for
this instance
+ * @throws java.io.IOException if this instance could not be serialized
+ */
+ public java.nio.ByteBuffer toByteBuffer() throws java.io.IOException {
+ return ENCODER.encode(this);
+ }
+
+ /**
+ * Deserializes a DateRecord from a ByteBuffer.
+ *
+ * @param b a byte buffer holding serialized data for
an instance of this class
+ * @return a DateRecord instance decoded from the
given buffer
+ * @throws java.io.IOException if the given bytes could not be
deserialized into an instance of this class
+ */
+ public static DateRecord fromByteBuffer(
+ java.nio.ByteBuffer b)
+ throws java.io.IOException {
+ return DECODER.decode(b);
+ }
+
+ private java.time.LocalDate date;
+ private java.time.Instant timestamp;
+
+ /**
+ * Default constructor. Note that this does not initialize fields to their
default values from the schema. If that
+ * is desired then one should use <code>newBuilder()</code>.
+ */
+ public DateRecord() {
+ }
+
+ /**
+ * All-args constructor.
+ *
+ * @param date The new value for date
+ * @param timestamp The new value for timestamp
+ */
+ public DateRecord(java.time.LocalDate date, java.time.Instant timestamp) {
+ this.date = date;
+ this.timestamp = timestamp;
+ }
+
+ @Override
+ public SpecificData getSpecificData() {
+ return MODEL$;
+ }
+
+ @Override
+ public org.apache.avro.Schema getSchema() {
+ return SCHEMA$;
+ }
+
+ // Used by DatumWriter. Applications should not call.
+ @Override
+ public Object get(int field$) {
+ switch (field$) {
+ case 0:
+ return date;
+ case 1:
+ return timestamp;
+ default:
+ throw new IndexOutOfBoundsException("Invalid index: " +
field$);
+ }
+ }
+
+ // Used by DatumReader. Applications should not call.
+ @Override
+ @SuppressWarnings(value = "unchecked")
+ public void put(int field$, Object value$) {
+ switch (field$) {
+ case 0:
+ date = (java.time.LocalDate) value$;
+ break;
+ case 1:
+ timestamp = (java.time.Instant) value$;
+ break;
+ default:
+ throw new IndexOutOfBoundsException("Invalid index: " +
field$);
+ }
+ }
+
+ /**
+ * Gets the value of the 'date' field.
+ *
+ * @return The value of the 'date' field.
+ */
+ public java.time.LocalDate getDate() {
+ return date;
+ }
+
+ /**
+ * Sets the value of the 'date' field.
+ *
+ * @param value the value to set.
+ */
+ public void setDate(java.time.LocalDate value) {
+ this.date = value;
+ }
+
+ /**
+ * Gets the value of the 'timestamp' field.
+ *
+ * @return The value of the 'timestamp' field.
+ */
+ public java.time.Instant getTimestamp() {
+ return timestamp;
+ }
+
+ /**
+ * Sets the value of the 'timestamp' field.
+ *
+ * @param value the value to set.
+ */
+ public void setTimestamp(java.time.Instant value) {
+ this.timestamp = value;
+ }
+
+ /**
+ * Creates a new DateRecord RecordBuilder.
+ *
+ * @return A new DateRecord RecordBuilder
+ */
+ public static Builder newBuilder() {
+ return new Builder();
+ }
+
+ /**
+ * Creates a new DateRecord RecordBuilder by copying an existing Builder.
+ *
+ * @param other The existing builder to copy.
+ * @return A new DateRecord RecordBuilder
+ */
+ public static Builder newBuilder(Builder other) {
+ if (other == null) {
+ return new Builder();
+ } else {
+ return new Builder(other);
+ }
+ }
+
+ /**
+ * Creates a new DateRecord RecordBuilder by copying an existing
DateRecord instance.
+ *
+ * @param other The existing instance to copy.
+ * @return A new DateRecord RecordBuilder
+ */
+ public static Builder newBuilder(DateRecord other) {
+ if (other == null) {
+ return new Builder();
+ } else {
+ return new
org.apache.camel.dataformat.avro.example.DateRecord.Builder(other);
+ }
+ }
+
+ /**
+ * RecordBuilder for DateRecord instances.
+ */
+ @org.apache.avro.specific.AvroGenerated
+ public static class Builder extends
org.apache.avro.specific.SpecificRecordBuilderBase<DateRecord>
+ implements org.apache.avro.data.RecordBuilder<DateRecord> {
+
+ private java.time.LocalDate date;
+ private java.time.Instant timestamp;
+
+ /** Creates a new Builder */
+ private Builder() {
+ super(SCHEMA$, MODEL$);
+ }
+
+ /**
+ * Creates a Builder by copying an existing Builder.
+ *
+ * @param other The existing Builder to copy.
+ */
+ private
Builder(org.apache.camel.dataformat.avro.example.DateRecord.Builder other) {
+ super(other);
+ if (isValidValue(fields()[0], other.date)) {
+ this.date = data().deepCopy(fields()[0].schema(), other.date);
+ fieldSetFlags()[0] = other.fieldSetFlags()[0];
+ }
+ if (isValidValue(fields()[1], other.timestamp)) {
+ this.timestamp = data().deepCopy(fields()[1].schema(),
other.timestamp);
+ fieldSetFlags()[1] = other.fieldSetFlags()[1];
+ }
+ }
+
+ /**
+ * Creates a Builder by copying an existing DateRecord instance
+ *
+ * @param other The existing instance to copy.
+ */
+ private Builder(org.apache.camel.dataformat.avro.example.DateRecord
other) {
+ super(SCHEMA$, MODEL$);
+ if (isValidValue(fields()[0], other.date)) {
+ this.date = data().deepCopy(fields()[0].schema(), other.date);
+ fieldSetFlags()[0] = true;
+ }
+ if (isValidValue(fields()[1], other.timestamp)) {
+ this.timestamp = data().deepCopy(fields()[1].schema(),
other.timestamp);
+ fieldSetFlags()[1] = true;
+ }
+ }
+
+ /**
+ * Gets the value of the 'date' field.
+ *
+ * @return The value.
+ */
+ public java.time.LocalDate getDate() {
+ return date;
+ }
+
+ /**
+ * Sets the value of the 'date' field.
+ *
+ * @param value The value of 'date'.
+ * @return This builder.
+ */
+ public org.apache.camel.dataformat.avro.example.DateRecord.Builder
setDate(java.time.LocalDate value) {
+ validate(fields()[0], value);
+ this.date = value;
+ fieldSetFlags()[0] = true;
+ return this;
+ }
+
+ /**
+ * Checks whether the 'date' field has been set.
+ *
+ * @return True if the 'date' field has been set, false otherwise.
+ */
+ public boolean hasDate() {
+ return fieldSetFlags()[0];
+ }
+
+ /**
+ * Clears the value of the 'date' field.
+ *
+ * @return This builder.
+ */
+ public org.apache.camel.dataformat.avro.example.DateRecord.Builder
clearDate() {
+ date = null;
+ fieldSetFlags()[0] = false;
+ return this;
+ }
+
+ /**
+ * Gets the value of the 'timestamp' field.
+ *
+ * @return The value.
+ */
+ public java.time.Instant getTimestamp() {
+ return timestamp;
+ }
+
+ /**
+ * Sets the value of the 'timestamp' field.
+ *
+ * @param value The value of 'timestamp'.
+ * @return This builder.
+ */
+ public org.apache.camel.dataformat.avro.example.DateRecord.Builder
setTimestamp(java.time.Instant value) {
+ validate(fields()[1], value);
+ this.timestamp = value;
+ fieldSetFlags()[1] = true;
+ return this;
+ }
+
+ /**
+ * Checks whether the 'timestamp' field has been set.
+ *
+ * @return True if the 'timestamp' field has been set, false otherwise.
+ */
+ public boolean hasTimestamp() {
+ return fieldSetFlags()[1];
+ }
+
+ /**
+ * Clears the value of the 'timestamp' field.
+ *
+ * @return This builder.
+ */
+ public org.apache.camel.dataformat.avro.example.DateRecord.Builder
clearTimestamp() {
+ timestamp = null;
+ fieldSetFlags()[1] = false;
+ return this;
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public DateRecord build() {
+ try {
+ DateRecord record = new DateRecord();
+ record.date = fieldSetFlags()[0] ? this.date :
(java.time.LocalDate) defaultValue(fields()[0]);
+ record.timestamp = fieldSetFlags()[1] ? this.timestamp :
(java.time.Instant) defaultValue(fields()[1]);
+ return record;
+ } catch (org.apache.avro.AvroMissingFieldException e) {
+ throw e;
+ } catch (java.lang.Exception e) {
+ throw new org.apache.avro.AvroRuntimeException(e);
+ }
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private static final org.apache.avro.io.DatumWriter<DateRecord> WRITER$
+ = (org.apache.avro.io.DatumWriter<DateRecord>)
MODEL$.createDatumWriter(SCHEMA$);
+
+ @Override
+ public void writeExternal(java.io.ObjectOutput out)
+ throws java.io.IOException {
+ WRITER$.write(this, SpecificData.getEncoder(out));
+ }
+
+ @SuppressWarnings("unchecked")
+ private static final org.apache.avro.io.DatumReader<DateRecord> READER$
+ = (org.apache.avro.io.DatumReader<DateRecord>)
MODEL$.createDatumReader(SCHEMA$);
+
+ @Override
+ public void readExternal(java.io.ObjectInput in)
+ throws java.io.IOException {
+ READER$.read(this, SpecificData.getDecoder(in));
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 1;
+ result = 31 * result + (date == null ? 0 : date.hashCode());
+ result = 31 * result + (timestamp == null ? 0 : timestamp.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof DateRecord)) {
+ return false;
+ }
+ DateRecord other = (DateRecord) o;
+ if (!java.util.Objects.equals(this.date, other.date)) {
+ return false;
+ }
+ if (!java.util.Objects.equals(this.timestamp, other.timestamp)) {
+ return false;
+ }
+ return true;
+ }
+}
diff --git
a/components/camel-avro/src/test/resources/org/apache/camel/dataformat/avro/date.avsc
b/components/camel-avro/src/test/resources/org/apache/camel/dataformat/avro/date.avsc
new file mode 100644
index 000000000000..d2fbf7ddef25
--- /dev/null
+++
b/components/camel-avro/src/test/resources/org/apache/camel/dataformat/avro/date.avsc
@@ -0,0 +1,29 @@
+{
+ "type": "record",
+ "name": "DateRecord",
+ "namespace": "org.apache.camel.dataformat.avro.example",
+ "fields": [
+ {
+ "name": "date",
+ "type": [
+ "null",
+ {
+ "type": "int",
+ "logicalType": "date"
+ }
+ ],
+ "default": null
+ },
+ {
+ "name": "timestamp",
+ "type": [
+ "null",
+ {
+ "type": "long",
+ "logicalType": "timestamp-millis"
+ }
+ ],
+ "default": null
+ }
+ ]
+}
\ No newline at end of file