From 2479f89b9cdd1adf4003ad5022a996ed1aa1be44 Mon Sep 17 00:00:00 2001 From: Auke van Leeuwen Date: Thu, 21 Sep 2017 10:01:48 +0200 Subject: [PATCH] AVRO-2079: Add ability to generate Java8 native date/time classes --- .../avro/data/Java8TimeConversions.java | 158 ++++ ...versions.java => JodaTimeConversions.java} | 2 +- .../avro/data/TestJava8TimeConversions.java | 243 +++++ ...ions.java => TestJodaTimeConversions.java} | 26 +- .../TestRecordWithJava8LogicalTypes.java | 893 ++++++++++++++++++ .../specific/TestRecordWithLogicalTypes.java | 6 +- .../specific/TestSpecificLogicalTypes.java | 107 ++- .../specific/TestSpecificToFromByteArray.java | 14 +- .../resources/record_with_logical_types.avsc | 2 +- .../avro/compiler/specific/ProtocolTask.java | 14 +- .../avro/compiler/specific/SchemaTask.java | 2 +- .../compiler/specific/SpecificCompiler.java | 74 +- .../specific/templates/java/classic/record.vm | 12 +- .../specific/TestSpecificCompiler.java | 56 +- .../apache/avro/mojo/AbstractAvroMojo.java | 17 + .../org/apache/avro/mojo/IDLProtocolMojo.java | 2 +- .../org/apache/avro/mojo/ProtocolMojo.java | 2 +- .../java/org/apache/avro/mojo/SchemaMojo.java | 2 +- .../java/maven-plugin/src/test/avro/User.avdl | 1 + .../java/maven-plugin/src/test/avro/User.avpr | 7 + .../java/maven-plugin/src/test/avro/User.avsc | 7 + .../avro/mojo/AbstractAvroMojoTest.java | 6 +- .../apache/avro/mojo/TestIDLProtocolMojo.java | 37 +- .../apache/avro/mojo/TestProtocolMojo.java | 33 +- .../org/apache/avro/mojo/TestSchemaMojo.java | 33 +- .../src/test/resources/unit/idl/pom-java8.xml | 67 ++ .../unit/idl/{pom.xml => pom-joda.xml} | 4 +- .../resources/unit/protocol/pom-java8.xml | 66 ++ .../unit/protocol/{pom.xml => pom-joda.xml} | 2 +- .../test/resources/unit/schema/pom-java8.xml | 65 ++ .../unit/schema/{pom.xml => pom-joda.xml} | 2 +- .../avro/tool/SpecificCompilerTool.java | 33 +- 32 files changed, 1914 insertions(+), 81 deletions(-) create mode 100644 lang/java/avro/src/main/java/org/apache/avro/data/Java8TimeConversions.java rename lang/java/avro/src/main/java/org/apache/avro/data/{TimeConversions.java => JodaTimeConversions.java} (99%) create mode 100644 lang/java/avro/src/test/java/org/apache/avro/data/TestJava8TimeConversions.java rename lang/java/avro/src/test/java/org/apache/avro/data/{TestTimeConversions.java => TestJodaTimeConversions.java} (91%) create mode 100644 lang/java/avro/src/test/java/org/apache/avro/specific/TestRecordWithJava8LogicalTypes.java create mode 100644 lang/java/maven-plugin/src/test/resources/unit/idl/pom-java8.xml rename lang/java/maven-plugin/src/test/resources/unit/idl/{pom.xml => pom-joda.xml} (92%) create mode 100644 lang/java/maven-plugin/src/test/resources/unit/protocol/pom-java8.xml rename lang/java/maven-plugin/src/test/resources/unit/protocol/{pom.xml => pom-joda.xml} (98%) create mode 100644 lang/java/maven-plugin/src/test/resources/unit/schema/pom-java8.xml rename lang/java/maven-plugin/src/test/resources/unit/schema/{pom.xml => pom-joda.xml} (98%) diff --git a/lang/java/avro/src/main/java/org/apache/avro/data/Java8TimeConversions.java b/lang/java/avro/src/main/java/org/apache/avro/data/Java8TimeConversions.java new file mode 100644 index 00000000000..b172bd88750 --- /dev/null +++ b/lang/java/avro/src/main/java/org/apache/avro/data/Java8TimeConversions.java @@ -0,0 +1,158 @@ +/** + * 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.avro.data; + +import org.apache.avro.Conversion; +import org.apache.avro.LogicalType; +import org.apache.avro.Schema; + +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalTime; +import java.util.concurrent.TimeUnit; + +public class Java8TimeConversions { + public static class DateConversion extends Conversion { + + @Override + public Class getConvertedType() { + return LocalDate.class; + } + + @Override + public String getLogicalTypeName() { + return "date"; + } + + @Override + public LocalDate fromInt(Integer daysFromEpoch, Schema schema, LogicalType type) { + return LocalDate.ofEpochDay(daysFromEpoch); + } + + @Override + public Integer toInt(LocalDate date, Schema schema, LogicalType type) { + long epochDays = date.toEpochDay(); + + return Math.toIntExact(epochDays); + } + } + + public static class TimeMillisConversion extends Conversion { + @Override + public Class getConvertedType() { + return LocalTime.class; + } + + @Override + public String getLogicalTypeName() { + return "time-millis"; + } + + @Override + public LocalTime fromInt(Integer millisFromMidnight, Schema schema, LogicalType type) { + return LocalTime.ofNanoOfDay(TimeUnit.MILLISECONDS.toNanos(millisFromMidnight)); + } + + @Override + public Integer toInt(LocalTime time, Schema schema, LogicalType type) { + return Math.toIntExact(TimeUnit.NANOSECONDS.toMillis(time.toNanoOfDay())); + } + } + + public static class TimeMicrosConversion extends Conversion { + @Override + public Class getConvertedType() { + return LocalTime.class; + } + + @Override + public String getLogicalTypeName() { + return "time-micros"; + } + + @Override + public LocalTime fromLong(Long microsFromMidnight, Schema schema, LogicalType type) { + return LocalTime.ofNanoOfDay(TimeUnit.MICROSECONDS.toNanos(microsFromMidnight)); + } + + @Override + public Long toLong(LocalTime time, Schema schema, LogicalType type) { + return TimeUnit.NANOSECONDS.toMicros(time.toNanoOfDay()); + } + } + + public static class TimestampMillisConversion extends Conversion { + @Override + public Class getConvertedType() { + return Instant.class; + } + + @Override + public String getLogicalTypeName() { + return "timestamp-millis"; + } + + @Override + public Instant fromLong(Long millisFromEpoch, Schema schema, LogicalType type) { + return Instant.ofEpochMilli(millisFromEpoch); + } + + @Override + public Long toLong(Instant timestamp, Schema schema, LogicalType type) { + return timestamp.toEpochMilli(); + } + } + + public static class TimestampMicrosConversion extends Conversion { + @Override + public Class getConvertedType() { + return Instant.class; + } + + @Override + public String getLogicalTypeName() { + return "timestamp-micros"; + } + + @Override + public Instant fromLong(Long microsFromEpoch, Schema schema, LogicalType type) { + long epochSeconds = microsFromEpoch / (1_000_000); + long nanoAdjustment = (microsFromEpoch % (1_000_000)) * 1_000; + + return Instant.ofEpochSecond(epochSeconds, nanoAdjustment); + } + + @Override + public Long toLong(Instant instant, Schema schema, LogicalType type) { + long seconds = instant.getEpochSecond(); + int nanos = instant.getNano(); + + if (seconds < 0 && nanos > 0) { + long micros = Math.multiplyExact(seconds + 1, 1_000_000); + long adjustment = (nanos / 1_000L) - 1_000_000; + + return Math.addExact(micros, adjustment); + } else { + long micros = Math.multiplyExact(seconds, 1_000_000); + + return Math.addExact(micros, nanos / 1_000); + } + } + } +} diff --git a/lang/java/avro/src/main/java/org/apache/avro/data/TimeConversions.java b/lang/java/avro/src/main/java/org/apache/avro/data/JodaTimeConversions.java similarity index 99% rename from lang/java/avro/src/main/java/org/apache/avro/data/TimeConversions.java rename to lang/java/avro/src/main/java/org/apache/avro/data/JodaTimeConversions.java index b53bb1f6928..769ebdd664f 100644 --- a/lang/java/avro/src/main/java/org/apache/avro/data/TimeConversions.java +++ b/lang/java/avro/src/main/java/org/apache/avro/data/JodaTimeConversions.java @@ -27,7 +27,7 @@ import org.joda.time.LocalDate; import org.joda.time.LocalTime; -public class TimeConversions { +public class JodaTimeConversions { public static class DateConversion extends Conversion { private static final LocalDate EPOCH_DATE = new LocalDate(1970, 1, 1); diff --git a/lang/java/avro/src/test/java/org/apache/avro/data/TestJava8TimeConversions.java b/lang/java/avro/src/test/java/org/apache/avro/data/TestJava8TimeConversions.java new file mode 100644 index 00000000000..5583308ae5f --- /dev/null +++ b/lang/java/avro/src/test/java/org/apache/avro/data/TestJava8TimeConversions.java @@ -0,0 +1,243 @@ +/** + * 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.avro.data; + +import org.apache.avro.LogicalTypes; +import org.apache.avro.Schema; +import org.apache.avro.data.Java8TimeConversions.DateConversion; +import org.apache.avro.data.Java8TimeConversions.TimeMillisConversion; +import org.apache.avro.data.Java8TimeConversions.TimeMicrosConversion; +import org.apache.avro.data.Java8TimeConversions.TimestampMicrosConversion; +import org.apache.avro.data.Java8TimeConversions.TimestampMillisConversion; + +import java.time.Instant; +import java.time.ZoneOffset; +import java.time.LocalDate; +import java.time.LocalTime; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.time.ZonedDateTime; +import java.util.Date; + +public class TestJava8TimeConversions { + + public static Schema DATE_SCHEMA; + public static Schema TIME_MILLIS_SCHEMA; + public static Schema TIME_MICROS_SCHEMA; + public static Schema TIMESTAMP_MILLIS_SCHEMA; + public static Schema TIMESTAMP_MICROS_SCHEMA; + + @BeforeClass + public static void createSchemas() { + TestJava8TimeConversions.DATE_SCHEMA = LogicalTypes.date() + .addToSchema(Schema.create(Schema.Type.INT)); + TestJava8TimeConversions.TIME_MILLIS_SCHEMA = LogicalTypes.timeMillis() + .addToSchema(Schema.create(Schema.Type.INT)); + TestJava8TimeConversions.TIME_MICROS_SCHEMA = LogicalTypes.timeMicros() + .addToSchema(Schema.create(Schema.Type.LONG)); + TestJava8TimeConversions.TIMESTAMP_MILLIS_SCHEMA = LogicalTypes.timestampMillis() + .addToSchema(Schema.create(Schema.Type.LONG)); + TestJava8TimeConversions.TIMESTAMP_MICROS_SCHEMA = LogicalTypes.timestampMicros() + .addToSchema(Schema.create(Schema.Type.LONG)); + } + + @Test + public void testDateConversion() throws Exception { + DateConversion conversion = new DateConversion(); + LocalDate Jan_6_1970 = LocalDate.of(1970, 1, 6); // 5 + LocalDate Jan_1_1970 = LocalDate.of(1970, 1, 1); // 0 + LocalDate Dec_27_1969 = LocalDate.of(1969, 12, 27); // -5 + + Assert.assertEquals("6 Jan 1970 should be 5", 5, + (int) conversion.toInt(Jan_6_1970, DATE_SCHEMA, LogicalTypes.date())); + Assert.assertEquals("1 Jan 1970 should be 0", 0, + (int) conversion.toInt(Jan_1_1970, DATE_SCHEMA, LogicalTypes.date())); + Assert.assertEquals("27 Dec 1969 should be -5", -5, + (int) conversion.toInt(Dec_27_1969, DATE_SCHEMA, LogicalTypes.date())); + + Assert.assertEquals("6 Jan 1970 should be 5", + conversion.fromInt(5, DATE_SCHEMA, LogicalTypes.date()), Jan_6_1970); + Assert.assertEquals("1 Jan 1970 should be 0", + conversion.fromInt(0, DATE_SCHEMA, LogicalTypes.date()), Jan_1_1970); + Assert.assertEquals("27 Dec 1969 should be -5", + conversion.fromInt(-5, DATE_SCHEMA, LogicalTypes.date()), Dec_27_1969); + } + + @Test + public void testTimeMillisConversion() throws Exception { + TimeMillisConversion conversion = new TimeMillisConversion(); + LocalTime oneAM = LocalTime.of(1, 0); + LocalTime afternoon = LocalTime.of(15, 14, 15, 926_000_000); + int afternoonMillis = ((15 * 60 + 14) * 60 + 15) * 1000 + 926; + + Assert.assertEquals("Midnight should be 0", 0, + (int) conversion.toInt( + LocalTime.MIDNIGHT, TIME_MILLIS_SCHEMA, LogicalTypes.timeMillis())); + Assert.assertEquals("01:00 should be 3,600,000", 3_600_000, + (int) conversion.toInt( + oneAM, TIME_MILLIS_SCHEMA, LogicalTypes.timeMillis())); + Assert.assertEquals("15:14:15.926 should be " + afternoonMillis, + afternoonMillis, + (int) conversion.toInt( + afternoon, TIME_MILLIS_SCHEMA, LogicalTypes.timeMillis())); + + Assert.assertEquals("Midnight should be 0", + LocalTime.MIDNIGHT, + conversion.fromInt(0, TIME_MILLIS_SCHEMA, LogicalTypes.timeMillis())); + Assert.assertEquals("01:00 should be 3,600,000", + oneAM, + conversion.fromInt( + 3600000, TIME_MILLIS_SCHEMA, LogicalTypes.timeMillis())); + Assert.assertEquals("15:14:15.926 should be " + afternoonMillis, + afternoon, + conversion.fromInt( + afternoonMillis, TIME_MILLIS_SCHEMA, LogicalTypes.timeMillis())); + } + + @Test + public void testTimeMicrosConversion() throws Exception { + TimeMicrosConversion conversion = new TimeMicrosConversion(); + LocalTime oneAM = LocalTime.of(1, 0); + LocalTime afternoon = LocalTime.of(15, 14, 15, 926_551_000); + long afternoonMicros = ((long) (15 * 60 + 14) * 60 + 15) * 1_000_000 + 926_551; + + Assert.assertEquals("Midnight should be 0", + LocalTime.MIDNIGHT, + conversion.fromLong(0L, TIME_MICROS_SCHEMA, LogicalTypes.timeMicros())); + Assert.assertEquals("01:00 should be 3,600,000,000", + oneAM, + conversion.fromLong( + 3_600_000_000L, TIME_MICROS_SCHEMA, LogicalTypes.timeMicros())); + Assert.assertEquals("15:14:15.926551 should be " + afternoonMicros, + afternoon, + conversion.fromLong( + afternoonMicros, TIME_MICROS_SCHEMA, LogicalTypes.timeMicros())); + + Assert.assertEquals("Midnight should be 0", 0, + (long) conversion.toLong( + LocalTime.MIDNIGHT, TIME_MICROS_SCHEMA, LogicalTypes.timeMicros())); + Assert.assertEquals("01:00 should be 3,600,000,000", 3_600_000_000L, + (long) conversion.toLong( + oneAM, TIME_MICROS_SCHEMA, LogicalTypes.timeMicros())); + Assert.assertEquals("15:14:15.926551 should be " + afternoonMicros, + afternoonMicros, + (long) conversion.toLong( + afternoon, TIME_MICROS_SCHEMA, LogicalTypes.timeMicros())); + } + + @Test + public void testTimestampMillisConversion() throws Exception { + TimestampMillisConversion conversion = new TimestampMillisConversion(); + long nowInstant = new Date().getTime(); // ms precision + + // round trip + Instant now = conversion.fromLong( + nowInstant, TIMESTAMP_MILLIS_SCHEMA, LogicalTypes.timestampMillis()); + long roundTrip = conversion.toLong( + now, TIMESTAMP_MILLIS_SCHEMA, LogicalTypes.timestampMillis()); + Assert.assertEquals("Round-trip conversion should work", + nowInstant, roundTrip); + + long May_28_2015_21_46_53_221_instant = 1432849613221L; + Instant May_28_2015_21_46_53_221 = + ZonedDateTime.of(2015, 5, 28, 21, 46, 53, 221_000_000, ZoneOffset.UTC).toInstant(); + + // known dates from https://www.epochconverter.com/ + // > Epoch + Assert.assertEquals("Known date should be correct", + May_28_2015_21_46_53_221, + conversion.fromLong(May_28_2015_21_46_53_221_instant, + TIMESTAMP_MILLIS_SCHEMA, LogicalTypes.timestampMillis())); + Assert.assertEquals("Known date should be correct", + May_28_2015_21_46_53_221_instant, + (long) conversion.toLong(May_28_2015_21_46_53_221, + TIMESTAMP_MILLIS_SCHEMA, LogicalTypes.timestampMillis())); + + // Epoch + Assert.assertEquals("1970-01-01 should be 0", + Instant.EPOCH, + conversion.fromLong(0L, + TIMESTAMP_MILLIS_SCHEMA, LogicalTypes.timestampMillis())); + Assert.assertEquals("1970-01-01 should be 0", + 0L, + (long) conversion.toLong(ZonedDateTime.ofInstant(Instant.EPOCH, ZoneOffset.UTC).toInstant(), + TIMESTAMP_MILLIS_SCHEMA, LogicalTypes.timestampMillis())); + + // < Epoch + long Jul_01_1969_12_00_00_123_instant = -15854400000L + 123; + Instant Jul_01_1969_12_00_00_123 = + ZonedDateTime.of(1969, 7, 1, 12, 0, 0, 123_000_000, ZoneOffset.UTC).toInstant(); + + Assert.assertEquals("Pre 1970 date should be correct", + Jul_01_1969_12_00_00_123, + conversion.fromLong(Jul_01_1969_12_00_00_123_instant, + TIMESTAMP_MILLIS_SCHEMA, LogicalTypes.timestampMillis())); + Assert.assertEquals("Pre 1970 date should be correct", + Jul_01_1969_12_00_00_123_instant, + (long) conversion.toLong(Jul_01_1969_12_00_00_123, + TIMESTAMP_MILLIS_SCHEMA, LogicalTypes.timestampMillis())); + } + + @Test + public void testTimestampMicrosConversion() throws Exception { + TimestampMicrosConversion conversion = new TimestampMicrosConversion(); + + // known dates from https://www.epochconverter.com/ + // > Epoch + long May_28_2015_21_46_53_221_843_instant = 1432849613221L * 1000 + 843; + Instant May_28_2015_21_46_53_221_843 = + ZonedDateTime.of(2015, 5, 28, 21, 46, 53, 221_843_000, ZoneOffset.UTC).toInstant(); + + Assert.assertEquals("Known date should be correct", + May_28_2015_21_46_53_221_843, + conversion.fromLong(May_28_2015_21_46_53_221_843_instant, + TIMESTAMP_MICROS_SCHEMA, LogicalTypes.timestampMicros())); + + Assert.assertEquals("Known date should be correct", + May_28_2015_21_46_53_221_843_instant, + (long) conversion.toLong(May_28_2015_21_46_53_221_843, + TIMESTAMP_MICROS_SCHEMA, LogicalTypes.timestampMillis())); + + // Epoch + Assert.assertEquals("1970-01-01 should be 0", + Instant.EPOCH, + conversion.fromLong(0L, + TIMESTAMP_MILLIS_SCHEMA, LogicalTypes.timestampMillis())); + Assert.assertEquals("1970-01-01 should be 0", + 0L, + (long) conversion.toLong(ZonedDateTime.ofInstant(Instant.EPOCH, ZoneOffset.UTC).toInstant(), + TIMESTAMP_MILLIS_SCHEMA, LogicalTypes.timestampMillis())); + + // < Epoch + long Jul_01_1969_12_00_00_000_123_instant = -15854400000L * 1000 + 123; + Instant Jul_01_1969_12_00_00_000_123 = + ZonedDateTime.of(1969, 7, 1, 12, 0, 0, 123_000, ZoneOffset.UTC).toInstant(); + + Assert.assertEquals("Pre 1970 date should be correct", + Jul_01_1969_12_00_00_000_123, + conversion.fromLong(Jul_01_1969_12_00_00_000_123_instant, + TIMESTAMP_MILLIS_SCHEMA, LogicalTypes.timestampMillis())); + Assert.assertEquals("Pre 1970 date should be correct", + Jul_01_1969_12_00_00_000_123_instant, + (long) conversion.toLong(Jul_01_1969_12_00_00_000_123, + TIMESTAMP_MILLIS_SCHEMA, LogicalTypes.timestampMillis())); + } +} diff --git a/lang/java/avro/src/test/java/org/apache/avro/data/TestTimeConversions.java b/lang/java/avro/src/test/java/org/apache/avro/data/TestJodaTimeConversions.java similarity index 91% rename from lang/java/avro/src/test/java/org/apache/avro/data/TestTimeConversions.java rename to lang/java/avro/src/test/java/org/apache/avro/data/TestJodaTimeConversions.java index 5e315cd1ea8..ca2bb50428d 100644 --- a/lang/java/avro/src/test/java/org/apache/avro/data/TestTimeConversions.java +++ b/lang/java/avro/src/test/java/org/apache/avro/data/TestJodaTimeConversions.java @@ -20,13 +20,13 @@ import org.apache.avro.LogicalTypes; import org.apache.avro.Schema; -import org.apache.avro.data.TimeConversions.DateConversion; -import org.apache.avro.data.TimeConversions.LossyTimeMicrosConversion; -import org.apache.avro.data.TimeConversions.LossyTimestampMicrosConversion; -import org.apache.avro.data.TimeConversions.TimeMicrosConversion; -import org.apache.avro.data.TimeConversions.TimestampMicrosConversion; -import org.apache.avro.data.TimeConversions.TimeConversion; -import org.apache.avro.data.TimeConversions.TimestampConversion; +import org.apache.avro.data.JodaTimeConversions.DateConversion; +import org.apache.avro.data.JodaTimeConversions.LossyTimeMicrosConversion; +import org.apache.avro.data.JodaTimeConversions.LossyTimestampMicrosConversion; +import org.apache.avro.data.JodaTimeConversions.TimeMicrosConversion; +import org.apache.avro.data.JodaTimeConversions.TimestampMicrosConversion; +import org.apache.avro.data.JodaTimeConversions.TimeConversion; +import org.apache.avro.data.JodaTimeConversions.TimestampConversion; import org.joda.time.DateTime; import org.joda.time.DateTimeZone; import org.joda.time.LocalDate; @@ -36,7 +36,7 @@ import org.junit.Test; import java.util.Date; -public class TestTimeConversions { +public class TestJodaTimeConversions { public static Schema DATE_SCHEMA; public static Schema TIME_MILLIS_SCHEMA; @@ -46,15 +46,15 @@ public class TestTimeConversions { @BeforeClass public static void createSchemas() { - TestTimeConversions.DATE_SCHEMA = LogicalTypes.date() + TestJodaTimeConversions.DATE_SCHEMA = LogicalTypes.date() .addToSchema(Schema.create(Schema.Type.INT)); - TestTimeConversions.TIME_MILLIS_SCHEMA = LogicalTypes.timeMillis() + TestJodaTimeConversions.TIME_MILLIS_SCHEMA = LogicalTypes.timeMillis() .addToSchema(Schema.create(Schema.Type.INT)); - TestTimeConversions.TIME_MICROS_SCHEMA = LogicalTypes.timeMicros() + TestJodaTimeConversions.TIME_MICROS_SCHEMA = LogicalTypes.timeMicros() .addToSchema(Schema.create(Schema.Type.LONG)); - TestTimeConversions.TIMESTAMP_MILLIS_SCHEMA = LogicalTypes.timestampMillis() + TestJodaTimeConversions.TIMESTAMP_MILLIS_SCHEMA = LogicalTypes.timestampMillis() .addToSchema(Schema.create(Schema.Type.LONG)); - TestTimeConversions.TIMESTAMP_MICROS_SCHEMA = LogicalTypes.timestampMicros() + TestJodaTimeConversions.TIMESTAMP_MICROS_SCHEMA = LogicalTypes.timestampMicros() .addToSchema(Schema.create(Schema.Type.LONG)); } diff --git a/lang/java/avro/src/test/java/org/apache/avro/specific/TestRecordWithJava8LogicalTypes.java b/lang/java/avro/src/test/java/org/apache/avro/specific/TestRecordWithJava8LogicalTypes.java new file mode 100644 index 00000000000..522a2c0254e --- /dev/null +++ b/lang/java/avro/src/test/java/org/apache/avro/specific/TestRecordWithJava8LogicalTypes.java @@ -0,0 +1,893 @@ +/** + * Autogenerated by Avro + * + * DO NOT EDIT DIRECTLY + */ +package org.apache.avro.specific; + +import org.apache.avro.specific.SpecificData; +import org.apache.avro.message.BinaryMessageEncoder; +import org.apache.avro.message.BinaryMessageDecoder; +import org.apache.avro.message.SchemaStore; + +@SuppressWarnings("all") +@org.apache.avro.specific.AvroGenerated +public class TestRecordWithJava8LogicalTypes extends org.apache.avro.specific.SpecificRecordBase implements org.apache.avro.specific.SpecificRecord { + private static final long serialVersionUID = 3313339903648295220L; + public static final org.apache.avro.Schema SCHEMA$ = new org.apache.avro.Schema.Parser().parse("{\"type\":\"record\",\"name\":\"TestRecordWithJava8LogicalTypes\",\"namespace\":\"org.apache.avro.specific\",\"fields\":[{\"name\":\"b\",\"type\":\"boolean\"},{\"name\":\"i32\",\"type\":\"int\"},{\"name\":\"i64\",\"type\":\"long\"},{\"name\":\"f32\",\"type\":\"float\"},{\"name\":\"f64\",\"type\":\"double\"},{\"name\":\"s\",\"type\":[\"null\",\"string\"],\"default\":null},{\"name\":\"d\",\"type\":{\"type\":\"int\",\"logicalType\":\"date\"}},{\"name\":\"t\",\"type\":{\"type\":\"int\",\"logicalType\":\"time-millis\"}},{\"name\":\"ts\",\"type\":{\"type\":\"long\",\"logicalType\":\"timestamp-millis\"}},{\"name\":\"dec\",\"type\":{\"type\":\"bytes\",\"logicalType\":\"decimal\",\"precision\":9,\"scale\":2}}]}"); + public static org.apache.avro.Schema getClassSchema() { return SCHEMA$; } + + private static SpecificData MODEL$ = new SpecificData(); + + private static final BinaryMessageEncoder ENCODER = + new BinaryMessageEncoder(MODEL$, SCHEMA$); + + private static final BinaryMessageDecoder DECODER = + new BinaryMessageDecoder(MODEL$, SCHEMA$); + + /** + * Return the BinaryMessageDecoder instance used by this class. + */ + public static BinaryMessageDecoder 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 + */ + public static BinaryMessageDecoder createDecoder(SchemaStore resolver) { + return new BinaryMessageDecoder(MODEL$, SCHEMA$, resolver); + } + + /** Serializes this TestRecordWithJava8LogicalTypes to a ByteBuffer. */ + public java.nio.ByteBuffer toByteBuffer() throws java.io.IOException { + return ENCODER.encode(this); + } + + /** Deserializes a TestRecordWithJava8LogicalTypes from a ByteBuffer. */ + public static TestRecordWithJava8LogicalTypes fromByteBuffer( + java.nio.ByteBuffer b) throws java.io.IOException { + return DECODER.decode(b); + } + + @Deprecated public boolean b; + @Deprecated public int i32; + @Deprecated public long i64; + @Deprecated public float f32; + @Deprecated public double f64; + @Deprecated public java.lang.CharSequence s; + @Deprecated public java.time.LocalDate d; + @Deprecated public java.time.LocalTime t; + @Deprecated public java.time.Instant ts; + @Deprecated public java.math.BigDecimal dec; + + /** + * Default constructor. Note that this does not initialize fields + * to their default values from the schema. If that is desired then + * one should use newBuilder(). + */ + public TestRecordWithJava8LogicalTypes() {} + + /** + * All-args constructor. + * @param b The new value for b + * @param i32 The new value for i32 + * @param i64 The new value for i64 + * @param f32 The new value for f32 + * @param f64 The new value for f64 + * @param s The new value for s + * @param d The new value for d + * @param t The new value for t + * @param ts The new value for ts + * @param dec The new value for dec + */ + public TestRecordWithJava8LogicalTypes(java.lang.Boolean b, java.lang.Integer i32, java.lang.Long i64, java.lang.Float f32, java.lang.Double f64, java.lang.CharSequence s, java.time.LocalDate d, java.time.LocalTime t, java.time.Instant ts, java.math.BigDecimal dec) { + this.b = b; + this.i32 = i32; + this.i64 = i64; + this.f32 = f32; + this.f64 = f64; + this.s = s; + this.d = d; + this.t = t; + this.ts = ts; + this.dec = dec; + } + + public org.apache.avro.Schema getSchema() { return SCHEMA$; } + // Used by DatumWriter. Applications should not call. + public java.lang.Object get(int field$) { + switch (field$) { + case 0: return b; + case 1: return i32; + case 2: return i64; + case 3: return f32; + case 4: return f64; + case 5: return s; + case 6: return d; + case 7: return t; + case 8: return ts; + case 9: return dec; + default: throw new org.apache.avro.AvroRuntimeException("Bad index"); + } + } + + protected static final org.apache.avro.Conversions.DecimalConversion DECIMAL_CONVERSION = new org.apache.avro.Conversions.DecimalConversion(); + protected static final org.apache.avro.data.Java8TimeConversions.DateConversion DATE_CONVERSION = new org.apache.avro.data.Java8TimeConversions.DateConversion(); + protected static final org.apache.avro.data.Java8TimeConversions.TimeMillisConversion TIME_CONVERSION = new org.apache.avro.data.Java8TimeConversions.TimeMillisConversion(); + protected static final org.apache.avro.data.Java8TimeConversions.TimestampMillisConversion TIMESTAMP_CONVERSION = new org.apache.avro.data.Java8TimeConversions.TimestampMillisConversion(); + + private static final org.apache.avro.Conversion[] conversions = + new org.apache.avro.Conversion[] { + null, + null, + null, + null, + null, + null, + DATE_CONVERSION, + TIME_CONVERSION, + TIMESTAMP_CONVERSION, + DECIMAL_CONVERSION, + null + }; + + @Override + public org.apache.avro.Conversion getConversion(int field) { + return conversions[field]; + } + + // Used by DatumReader. Applications should not call. + @SuppressWarnings(value="unchecked") + public void put(int field$, java.lang.Object value$) { + switch (field$) { + case 0: b = (java.lang.Boolean)value$; break; + case 1: i32 = (java.lang.Integer)value$; break; + case 2: i64 = (java.lang.Long)value$; break; + case 3: f32 = (java.lang.Float)value$; break; + case 4: f64 = (java.lang.Double)value$; break; + case 5: s = (java.lang.CharSequence)value$; break; + case 6: d = (java.time.LocalDate)value$; break; + case 7: t = (java.time.LocalTime)value$; break; + case 8: ts = (java.time.Instant)value$; break; + case 9: dec = (java.math.BigDecimal)value$; break; + default: throw new org.apache.avro.AvroRuntimeException("Bad index"); + } + } + + /** + * Gets the value of the 'b' field. + * @return The value of the 'b' field. + */ + public java.lang.Boolean getB() { + return b; + } + + /** + * Sets the value of the 'b' field. + * @param value the value to set. + */ + public void setB(java.lang.Boolean value) { + this.b = value; + } + + /** + * Gets the value of the 'i32' field. + * @return The value of the 'i32' field. + */ + public java.lang.Integer getI32() { + return i32; + } + + /** + * Sets the value of the 'i32' field. + * @param value the value to set. + */ + public void setI32(java.lang.Integer value) { + this.i32 = value; + } + + /** + * Gets the value of the 'i64' field. + * @return The value of the 'i64' field. + */ + public java.lang.Long getI64() { + return i64; + } + + /** + * Sets the value of the 'i64' field. + * @param value the value to set. + */ + public void setI64(java.lang.Long value) { + this.i64 = value; + } + + /** + * Gets the value of the 'f32' field. + * @return The value of the 'f32' field. + */ + public java.lang.Float getF32() { + return f32; + } + + /** + * Sets the value of the 'f32' field. + * @param value the value to set. + */ + public void setF32(java.lang.Float value) { + this.f32 = value; + } + + /** + * Gets the value of the 'f64' field. + * @return The value of the 'f64' field. + */ + public java.lang.Double getF64() { + return f64; + } + + /** + * Sets the value of the 'f64' field. + * @param value the value to set. + */ + public void setF64(java.lang.Double value) { + this.f64 = value; + } + + /** + * Gets the value of the 's' field. + * @return The value of the 's' field. + */ + public java.lang.CharSequence getS() { + return s; + } + + /** + * Sets the value of the 's' field. + * @param value the value to set. + */ + public void setS(java.lang.CharSequence value) { + this.s = value; + } + + /** + * Gets the value of the 'd' field. + * @return The value of the 'd' field. + */ + public java.time.LocalDate getD() { + return d; + } + + /** + * Sets the value of the 'd' field. + * @param value the value to set. + */ + public void setD(java.time.LocalDate value) { + this.d = value; + } + + /** + * Gets the value of the 't' field. + * @return The value of the 't' field. + */ + public java.time.LocalTime getT() { + return t; + } + + /** + * Sets the value of the 't' field. + * @param value the value to set. + */ + public void setT(java.time.LocalTime value) { + this.t = value; + } + + /** + * Gets the value of the 'ts' field. + * @return The value of the 'ts' field. + */ + public java.time.Instant getTs() { + return ts; + } + + /** + * Sets the value of the 'ts' field. + * @param value the value to set. + */ + public void setTs(java.time.Instant value) { + this.ts = value; + } + + /** + * Gets the value of the 'dec' field. + * @return The value of the 'dec' field. + */ + public java.math.BigDecimal getDec() { + return dec; + } + + /** + * Sets the value of the 'dec' field. + * @param value the value to set. + */ + public void setDec(java.math.BigDecimal value) { + this.dec = value; + } + + /** + * Creates a new TestRecordWithJava8LogicalTypes RecordBuilder. + * @return A new TestRecordWithJava8LogicalTypes RecordBuilder + */ + public static org.apache.avro.specific.TestRecordWithJava8LogicalTypes.Builder newBuilder() { + return new org.apache.avro.specific.TestRecordWithJava8LogicalTypes.Builder(); + } + + /** + * Creates a new TestRecordWithJava8LogicalTypes RecordBuilder by copying an existing Builder. + * @param other The existing builder to copy. + * @return A new TestRecordWithJava8LogicalTypes RecordBuilder + */ + public static org.apache.avro.specific.TestRecordWithJava8LogicalTypes.Builder newBuilder(org.apache.avro.specific.TestRecordWithJava8LogicalTypes.Builder other) { + if (other == null) { + return new org.apache.avro.specific.TestRecordWithJava8LogicalTypes.Builder(); + } else { + return new org.apache.avro.specific.TestRecordWithJava8LogicalTypes.Builder(other); + } + } + + /** + * Creates a new TestRecordWithJava8LogicalTypes RecordBuilder by copying an existing TestRecordWithJava8LogicalTypes instance. + * @param other The existing instance to copy. + * @return A new TestRecordWithJava8LogicalTypes RecordBuilder + */ + public static org.apache.avro.specific.TestRecordWithJava8LogicalTypes.Builder newBuilder(org.apache.avro.specific.TestRecordWithJava8LogicalTypes other) { + if (other == null) { + return new org.apache.avro.specific.TestRecordWithJava8LogicalTypes.Builder(); + } else { + return new org.apache.avro.specific.TestRecordWithJava8LogicalTypes.Builder(other); + } + } + + /** + * RecordBuilder for TestRecordWithJava8LogicalTypes instances. + */ + public static class Builder extends org.apache.avro.specific.SpecificRecordBuilderBase + implements org.apache.avro.data.RecordBuilder { + + private boolean b; + private int i32; + private long i64; + private float f32; + private double f64; + private java.lang.CharSequence s; + private java.time.LocalDate d; + private java.time.LocalTime t; + private java.time.Instant ts; + private java.math.BigDecimal dec; + + /** Creates a new Builder */ + private Builder() { + super(SCHEMA$); + } + + /** + * Creates a Builder by copying an existing Builder. + * @param other The existing Builder to copy. + */ + private Builder(org.apache.avro.specific.TestRecordWithJava8LogicalTypes.Builder other) { + super(other); + if (isValidValue(fields()[0], other.b)) { + this.b = data().deepCopy(fields()[0].schema(), other.b); + fieldSetFlags()[0] = other.fieldSetFlags()[0]; + } + if (isValidValue(fields()[1], other.i32)) { + this.i32 = data().deepCopy(fields()[1].schema(), other.i32); + fieldSetFlags()[1] = other.fieldSetFlags()[1]; + } + if (isValidValue(fields()[2], other.i64)) { + this.i64 = data().deepCopy(fields()[2].schema(), other.i64); + fieldSetFlags()[2] = other.fieldSetFlags()[2]; + } + if (isValidValue(fields()[3], other.f32)) { + this.f32 = data().deepCopy(fields()[3].schema(), other.f32); + fieldSetFlags()[3] = other.fieldSetFlags()[3]; + } + if (isValidValue(fields()[4], other.f64)) { + this.f64 = data().deepCopy(fields()[4].schema(), other.f64); + fieldSetFlags()[4] = other.fieldSetFlags()[4]; + } + if (isValidValue(fields()[5], other.s)) { + this.s = data().deepCopy(fields()[5].schema(), other.s); + fieldSetFlags()[5] = other.fieldSetFlags()[5]; + } + if (isValidValue(fields()[6], other.d)) { + this.d = data().deepCopy(fields()[6].schema(), other.d); + fieldSetFlags()[6] = other.fieldSetFlags()[6]; + } + if (isValidValue(fields()[7], other.t)) { + this.t = data().deepCopy(fields()[7].schema(), other.t); + fieldSetFlags()[7] = other.fieldSetFlags()[7]; + } + if (isValidValue(fields()[8], other.ts)) { + this.ts = data().deepCopy(fields()[8].schema(), other.ts); + fieldSetFlags()[8] = other.fieldSetFlags()[8]; + } + if (isValidValue(fields()[9], other.dec)) { + this.dec = data().deepCopy(fields()[9].schema(), other.dec); + fieldSetFlags()[9] = other.fieldSetFlags()[9]; + } + } + + /** + * Creates a Builder by copying an existing TestRecordWithJava8LogicalTypes instance + * @param other The existing instance to copy. + */ + private Builder(org.apache.avro.specific.TestRecordWithJava8LogicalTypes other) { + super(SCHEMA$); + if (isValidValue(fields()[0], other.b)) { + this.b = data().deepCopy(fields()[0].schema(), other.b); + fieldSetFlags()[0] = true; + } + if (isValidValue(fields()[1], other.i32)) { + this.i32 = data().deepCopy(fields()[1].schema(), other.i32); + fieldSetFlags()[1] = true; + } + if (isValidValue(fields()[2], other.i64)) { + this.i64 = data().deepCopy(fields()[2].schema(), other.i64); + fieldSetFlags()[2] = true; + } + if (isValidValue(fields()[3], other.f32)) { + this.f32 = data().deepCopy(fields()[3].schema(), other.f32); + fieldSetFlags()[3] = true; + } + if (isValidValue(fields()[4], other.f64)) { + this.f64 = data().deepCopy(fields()[4].schema(), other.f64); + fieldSetFlags()[4] = true; + } + if (isValidValue(fields()[5], other.s)) { + this.s = data().deepCopy(fields()[5].schema(), other.s); + fieldSetFlags()[5] = true; + } + if (isValidValue(fields()[6], other.d)) { + this.d = data().deepCopy(fields()[6].schema(), other.d); + fieldSetFlags()[6] = true; + } + if (isValidValue(fields()[7], other.t)) { + this.t = data().deepCopy(fields()[7].schema(), other.t); + fieldSetFlags()[7] = true; + } + if (isValidValue(fields()[8], other.ts)) { + this.ts = data().deepCopy(fields()[8].schema(), other.ts); + fieldSetFlags()[8] = true; + } + if (isValidValue(fields()[9], other.dec)) { + this.dec = data().deepCopy(fields()[9].schema(), other.dec); + fieldSetFlags()[9] = true; + } + } + + /** + * Gets the value of the 'b' field. + * @return The value. + */ + public java.lang.Boolean getB() { + return b; + } + + /** + * Sets the value of the 'b' field. + * @param value The value of 'b'. + * @return This builder. + */ + public org.apache.avro.specific.TestRecordWithJava8LogicalTypes.Builder setB(boolean value) { + validate(fields()[0], value); + this.b = value; + fieldSetFlags()[0] = true; + return this; + } + + /** + * Checks whether the 'b' field has been set. + * @return True if the 'b' field has been set, false otherwise. + */ + public boolean hasB() { + return fieldSetFlags()[0]; + } + + + /** + * Clears the value of the 'b' field. + * @return This builder. + */ + public org.apache.avro.specific.TestRecordWithJava8LogicalTypes.Builder clearB() { + fieldSetFlags()[0] = false; + return this; + } + + /** + * Gets the value of the 'i32' field. + * @return The value. + */ + public java.lang.Integer getI32() { + return i32; + } + + /** + * Sets the value of the 'i32' field. + * @param value The value of 'i32'. + * @return This builder. + */ + public org.apache.avro.specific.TestRecordWithJava8LogicalTypes.Builder setI32(int value) { + validate(fields()[1], value); + this.i32 = value; + fieldSetFlags()[1] = true; + return this; + } + + /** + * Checks whether the 'i32' field has been set. + * @return True if the 'i32' field has been set, false otherwise. + */ + public boolean hasI32() { + return fieldSetFlags()[1]; + } + + + /** + * Clears the value of the 'i32' field. + * @return This builder. + */ + public org.apache.avro.specific.TestRecordWithJava8LogicalTypes.Builder clearI32() { + fieldSetFlags()[1] = false; + return this; + } + + /** + * Gets the value of the 'i64' field. + * @return The value. + */ + public java.lang.Long getI64() { + return i64; + } + + /** + * Sets the value of the 'i64' field. + * @param value The value of 'i64'. + * @return This builder. + */ + public org.apache.avro.specific.TestRecordWithJava8LogicalTypes.Builder setI64(long value) { + validate(fields()[2], value); + this.i64 = value; + fieldSetFlags()[2] = true; + return this; + } + + /** + * Checks whether the 'i64' field has been set. + * @return True if the 'i64' field has been set, false otherwise. + */ + public boolean hasI64() { + return fieldSetFlags()[2]; + } + + + /** + * Clears the value of the 'i64' field. + * @return This builder. + */ + public org.apache.avro.specific.TestRecordWithJava8LogicalTypes.Builder clearI64() { + fieldSetFlags()[2] = false; + return this; + } + + /** + * Gets the value of the 'f32' field. + * @return The value. + */ + public java.lang.Float getF32() { + return f32; + } + + /** + * Sets the value of the 'f32' field. + * @param value The value of 'f32'. + * @return This builder. + */ + public org.apache.avro.specific.TestRecordWithJava8LogicalTypes.Builder setF32(float value) { + validate(fields()[3], value); + this.f32 = value; + fieldSetFlags()[3] = true; + return this; + } + + /** + * Checks whether the 'f32' field has been set. + * @return True if the 'f32' field has been set, false otherwise. + */ + public boolean hasF32() { + return fieldSetFlags()[3]; + } + + + /** + * Clears the value of the 'f32' field. + * @return This builder. + */ + public org.apache.avro.specific.TestRecordWithJava8LogicalTypes.Builder clearF32() { + fieldSetFlags()[3] = false; + return this; + } + + /** + * Gets the value of the 'f64' field. + * @return The value. + */ + public java.lang.Double getF64() { + return f64; + } + + /** + * Sets the value of the 'f64' field. + * @param value The value of 'f64'. + * @return This builder. + */ + public org.apache.avro.specific.TestRecordWithJava8LogicalTypes.Builder setF64(double value) { + validate(fields()[4], value); + this.f64 = value; + fieldSetFlags()[4] = true; + return this; + } + + /** + * Checks whether the 'f64' field has been set. + * @return True if the 'f64' field has been set, false otherwise. + */ + public boolean hasF64() { + return fieldSetFlags()[4]; + } + + + /** + * Clears the value of the 'f64' field. + * @return This builder. + */ + public org.apache.avro.specific.TestRecordWithJava8LogicalTypes.Builder clearF64() { + fieldSetFlags()[4] = false; + return this; + } + + /** + * Gets the value of the 's' field. + * @return The value. + */ + public java.lang.CharSequence getS() { + return s; + } + + /** + * Sets the value of the 's' field. + * @param value The value of 's'. + * @return This builder. + */ + public org.apache.avro.specific.TestRecordWithJava8LogicalTypes.Builder setS(java.lang.CharSequence value) { + validate(fields()[5], value); + this.s = value; + fieldSetFlags()[5] = true; + return this; + } + + /** + * Checks whether the 's' field has been set. + * @return True if the 's' field has been set, false otherwise. + */ + public boolean hasS() { + return fieldSetFlags()[5]; + } + + + /** + * Clears the value of the 's' field. + * @return This builder. + */ + public org.apache.avro.specific.TestRecordWithJava8LogicalTypes.Builder clearS() { + s = null; + fieldSetFlags()[5] = false; + return this; + } + + /** + * Gets the value of the 'd' field. + * @return The value. + */ + public java.time.LocalDate getD() { + return d; + } + + /** + * Sets the value of the 'd' field. + * @param value The value of 'd'. + * @return This builder. + */ + public org.apache.avro.specific.TestRecordWithJava8LogicalTypes.Builder setD(java.time.LocalDate value) { + validate(fields()[6], value); + this.d = value; + fieldSetFlags()[6] = true; + return this; + } + + /** + * Checks whether the 'd' field has been set. + * @return True if the 'd' field has been set, false otherwise. + */ + public boolean hasD() { + return fieldSetFlags()[6]; + } + + + /** + * Clears the value of the 'd' field. + * @return This builder. + */ + public org.apache.avro.specific.TestRecordWithJava8LogicalTypes.Builder clearD() { + fieldSetFlags()[6] = false; + return this; + } + + /** + * Gets the value of the 't' field. + * @return The value. + */ + public java.time.LocalTime getT() { + return t; + } + + /** + * Sets the value of the 't' field. + * @param value The value of 't'. + * @return This builder. + */ + public org.apache.avro.specific.TestRecordWithJava8LogicalTypes.Builder setT(java.time.LocalTime value) { + validate(fields()[7], value); + this.t = value; + fieldSetFlags()[7] = true; + return this; + } + + /** + * Checks whether the 't' field has been set. + * @return True if the 't' field has been set, false otherwise. + */ + public boolean hasT() { + return fieldSetFlags()[7]; + } + + + /** + * Clears the value of the 't' field. + * @return This builder. + */ + public org.apache.avro.specific.TestRecordWithJava8LogicalTypes.Builder clearT() { + fieldSetFlags()[7] = false; + return this; + } + + /** + * Gets the value of the 'ts' field. + * @return The value. + */ + public java.time.Instant getTs() { + return ts; + } + + /** + * Sets the value of the 'ts' field. + * @param value The value of 'ts'. + * @return This builder. + */ + public org.apache.avro.specific.TestRecordWithJava8LogicalTypes.Builder setTs(java.time.Instant value) { + validate(fields()[8], value); + this.ts = value; + fieldSetFlags()[8] = true; + return this; + } + + /** + * Checks whether the 'ts' field has been set. + * @return True if the 'ts' field has been set, false otherwise. + */ + public boolean hasTs() { + return fieldSetFlags()[8]; + } + + + /** + * Clears the value of the 'ts' field. + * @return This builder. + */ + public org.apache.avro.specific.TestRecordWithJava8LogicalTypes.Builder clearTs() { + fieldSetFlags()[8] = false; + return this; + } + + /** + * Gets the value of the 'dec' field. + * @return The value. + */ + public java.math.BigDecimal getDec() { + return dec; + } + + /** + * Sets the value of the 'dec' field. + * @param value The value of 'dec'. + * @return This builder. + */ + public org.apache.avro.specific.TestRecordWithJava8LogicalTypes.Builder setDec(java.math.BigDecimal value) { + validate(fields()[9], value); + this.dec = value; + fieldSetFlags()[9] = true; + return this; + } + + /** + * Checks whether the 'dec' field has been set. + * @return True if the 'dec' field has been set, false otherwise. + */ + public boolean hasDec() { + return fieldSetFlags()[9]; + } + + + /** + * Clears the value of the 'dec' field. + * @return This builder. + */ + public org.apache.avro.specific.TestRecordWithJava8LogicalTypes.Builder clearDec() { + dec = null; + fieldSetFlags()[9] = false; + return this; + } + + @Override + @SuppressWarnings("unchecked") + public TestRecordWithJava8LogicalTypes build() { + try { + TestRecordWithJava8LogicalTypes record = new TestRecordWithJava8LogicalTypes(); + record.b = fieldSetFlags()[0] ? this.b : (java.lang.Boolean) defaultValue(fields()[0], record.getConversion(0)); + record.i32 = fieldSetFlags()[1] ? this.i32 : (java.lang.Integer) defaultValue(fields()[1], record.getConversion(1)); + record.i64 = fieldSetFlags()[2] ? this.i64 : (java.lang.Long) defaultValue(fields()[2], record.getConversion(2)); + record.f32 = fieldSetFlags()[3] ? this.f32 : (java.lang.Float) defaultValue(fields()[3], record.getConversion(3)); + record.f64 = fieldSetFlags()[4] ? this.f64 : (java.lang.Double) defaultValue(fields()[4], record.getConversion(4)); + record.s = fieldSetFlags()[5] ? this.s : (java.lang.CharSequence) defaultValue(fields()[5], record.getConversion(5)); + record.d = fieldSetFlags()[6] ? this.d : (java.time.LocalDate) defaultValue(fields()[6], record.getConversion(6)); + record.t = fieldSetFlags()[7] ? this.t : (java.time.LocalTime) defaultValue(fields()[7], record.getConversion(7)); + record.ts = fieldSetFlags()[8] ? this.ts : (java.time.Instant) defaultValue(fields()[8], record.getConversion(8)); + record.dec = fieldSetFlags()[9] ? this.dec : (java.math.BigDecimal) defaultValue(fields()[9], record.getConversion(9)); + return record; + } catch (java.lang.Exception e) { + throw new org.apache.avro.AvroRuntimeException(e); + } + } + } + + @SuppressWarnings("unchecked") + private static final org.apache.avro.io.DatumWriter + WRITER$ = (org.apache.avro.io.DatumWriter)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 + READER$ = (org.apache.avro.io.DatumReader)MODEL$.createDatumReader(SCHEMA$); + + @Override public void readExternal(java.io.ObjectInput in) + throws java.io.IOException { + READER$.read(this, SpecificData.getDecoder(in)); + } + +} diff --git a/lang/java/avro/src/test/java/org/apache/avro/specific/TestRecordWithLogicalTypes.java b/lang/java/avro/src/test/java/org/apache/avro/specific/TestRecordWithLogicalTypes.java index da3f878bd67..e4853d312bb 100644 --- a/lang/java/avro/src/test/java/org/apache/avro/specific/TestRecordWithLogicalTypes.java +++ b/lang/java/avro/src/test/java/org/apache/avro/specific/TestRecordWithLogicalTypes.java @@ -264,9 +264,9 @@ public void setTs(org.joda.time.DateTime value) { this.ts = value; } - protected static final org.apache.avro.data.TimeConversions.DateConversion DATE_CONVERSION = new org.apache.avro.data.TimeConversions.DateConversion(); - protected static final org.apache.avro.data.TimeConversions.TimeConversion TIME_CONVERSION = new org.apache.avro.data.TimeConversions.TimeConversion(); - protected static final org.apache.avro.data.TimeConversions.TimestampConversion TIMESTAMP_CONVERSION = new org.apache.avro.data.TimeConversions.TimestampConversion(); + protected static final org.apache.avro.data.JodaTimeConversions.DateConversion DATE_CONVERSION = new org.apache.avro.data.JodaTimeConversions.DateConversion(); + protected static final org.apache.avro.data.JodaTimeConversions.TimeConversion TIME_CONVERSION = new org.apache.avro.data.JodaTimeConversions.TimeConversion(); + protected static final org.apache.avro.data.JodaTimeConversions.TimestampConversion TIMESTAMP_CONVERSION = new org.apache.avro.data.JodaTimeConversions.TimestampConversion(); protected static final org.apache.avro.Conversions.DecimalConversion DECIMAL_CONVERSION = new org.apache.avro.Conversions.DecimalConversion(); private final org.apache.avro.Conversion[] conversions = new org.apache.avro.Conversion[] { diff --git a/lang/java/avro/src/test/java/org/apache/avro/specific/TestSpecificLogicalTypes.java b/lang/java/avro/src/test/java/org/apache/avro/specific/TestSpecificLogicalTypes.java index 64d45cb5e4e..ccaec55e1b5 100644 --- a/lang/java/avro/src/test/java/org/apache/avro/specific/TestSpecificLogicalTypes.java +++ b/lang/java/avro/src/test/java/org/apache/avro/specific/TestSpecificLogicalTypes.java @@ -20,9 +20,9 @@ import org.apache.avro.Conversions; import org.apache.avro.LogicalTypes; import org.apache.avro.Schema; -import org.apache.avro.data.TimeConversions.DateConversion; -import org.apache.avro.data.TimeConversions.TimeConversion; -import org.apache.avro.data.TimeConversions.TimestampConversion; +import org.apache.avro.data.JodaTimeConversions.DateConversion; +import org.apache.avro.data.JodaTimeConversions.TimeConversion; +import org.apache.avro.data.JodaTimeConversions.TimestampConversion; import org.apache.avro.file.DataFileReader; import org.apache.avro.file.DataFileWriter; import org.apache.avro.file.FileReader; @@ -39,9 +39,15 @@ import java.io.File; import java.io.IOException; import java.math.BigDecimal; +import java.time.ZoneOffset; import java.util.ArrayList; import java.util.List; +import static org.hamcrest.Matchers.comparesEqualTo; +import static org.hamcrest.Matchers.empty; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; + /** * This tests compatibility between classes generated before and after * AVRO-1684. TestRecordWithoutLogicalTypes and TestRecordWithLogicalTypes were @@ -53,6 +59,10 @@ * Avro with existing Avro-generated sources. When using classes generated * before AVRO-1684, logical types should not be applied by the read or write * paths. Those files should behave as they did before. + * + * For AVRO-2079 {@link TestRecordWithJava8LogicalTypes} was generated from + * the same schema and tests were added to test compatibility between the + * two versions. */ public class TestSpecificLogicalTypes { @@ -80,6 +90,97 @@ public void testRecordWithLogicalTypes() throws IOException { Assert.assertEquals("Should match written record", record, actual.get(0)); } + @Test + public void testRecordWithJava8LogicalTypes() throws IOException { + TestRecordWithJava8LogicalTypes record = new TestRecordWithJava8LogicalTypes( + true, + 34, + 35L, + 3.14F, + 3019.34, + null, + java.time.LocalDate.now(), + java.time.LocalTime.now(), + java.time.Instant.now(), + new BigDecimal(123.45f).setScale(2, BigDecimal.ROUND_HALF_DOWN) + ); + + File data = write(TestRecordWithJava8LogicalTypes.getClassSchema(), record); + List actual = read( + TestRecordWithJava8LogicalTypes.getClassSchema(), data); + + Assert.assertEquals("Should match written record", record, actual.get(0)); + } + + @Test + public void testAbilityToReadJava8RecordWrittenAsJodaRecord() throws IOException { + TestRecordWithLogicalTypes withJoda = new TestRecordWithLogicalTypes( + true, + 34, + 35L, + 3.14F, + 3019.34, + null, + LocalDate.now(), + LocalTime.now(), + DateTime.now().withZone(DateTimeZone.UTC), + new BigDecimal(123.45f).setScale(2, BigDecimal.ROUND_HALF_DOWN) + ); + + File data = write(TestRecordWithLogicalTypes.getClassSchema(), withJoda); + List actual = read( + TestRecordWithJava8LogicalTypes.getClassSchema(), data); + + Assert.assertThat(actual, is(not(empty()))); + TestRecordWithJava8LogicalTypes withJava8 = actual.get(0); + + Assert.assertThat(withJava8.getB(), is(withJoda.getB())); + Assert.assertThat(withJava8.getI32(), is(withJoda.getI32())); + Assert.assertThat(withJava8.getI64(), is(withJoda.getI64())); + Assert.assertThat(withJava8.getF32(), is(withJoda.getF32())); + Assert.assertThat(withJava8.getF64(), is(withJoda.getF64())); + Assert.assertThat(withJava8.getS(), is(withJoda.getS())); + // all of these print in the ISO-8601 format + Assert.assertThat(withJava8.getD().toString(), is(withJoda.getD().toString())); + Assert.assertThat(withJava8.getT().toString(), is(withJoda.getT().toString())); + Assert.assertThat(withJava8.getTs().toString(), is(withJoda.getTs().toString())); + Assert.assertThat(withJava8.getDec(), comparesEqualTo(withJoda.getDec())); + } + + @Test + public void testAbilityToReadJodaRecordWrittenAsJava8Record() throws IOException { + TestRecordWithJava8LogicalTypes withJava8 = new TestRecordWithJava8LogicalTypes( + true, + 34, + 35L, + 3.14F, + 3019.34, + null, + java.time.LocalDate.now(), + java.time.LocalTime.now(), + java.time.Instant.now(), + new BigDecimal(123.45f).setScale(2, BigDecimal.ROUND_HALF_DOWN) + ); + + File data = write(TestRecordWithJava8LogicalTypes.getClassSchema(), withJava8); + List actual = read( + TestRecordWithLogicalTypes.getClassSchema(), data); + + Assert.assertThat(actual, is(not(empty()))); + TestRecordWithLogicalTypes withJoda = actual.get(0); + + Assert.assertThat(withJoda.getB(), is(withJava8.getB())); + Assert.assertThat(withJoda.getI32(), is(withJava8.getI32())); + Assert.assertThat(withJoda.getI64(), is(withJava8.getI64())); + Assert.assertThat(withJoda.getF32(), is(withJava8.getF32())); + Assert.assertThat(withJoda.getF64(), is(withJava8.getF64())); + Assert.assertThat(withJoda.getS(), is(withJava8.getS())); + // all of these print in the ISO-8601 format + Assert.assertThat(withJoda.getD().toString(), is(withJava8.getD().toString())); + Assert.assertThat(withJoda.getT().toString(), is(withJava8.getT().toString())); + Assert.assertThat(withJoda.getTs().toString(), is(withJava8.getTs().toString())); + Assert.assertThat(withJoda.getDec(), comparesEqualTo(withJava8.getDec())); + } @Test public void testRecordWithoutLogicalTypes() throws IOException { diff --git a/lang/java/avro/src/test/java/org/apache/avro/specific/TestSpecificToFromByteArray.java b/lang/java/avro/src/test/java/org/apache/avro/specific/TestSpecificToFromByteArray.java index d3a81c315e7..8773cdbdc0c 100644 --- a/lang/java/avro/src/test/java/org/apache/avro/specific/TestSpecificToFromByteArray.java +++ b/lang/java/avro/src/test/java/org/apache/avro/specific/TestSpecificToFromByteArray.java @@ -19,7 +19,7 @@ import org.apache.avro.Conversions; import org.apache.avro.LogicalTypes; -import org.apache.avro.data.TimeConversions; +import org.apache.avro.data.JodaTimeConversions; import org.apache.avro.message.MissingSchemaException; import org.joda.time.DateTime; import org.joda.time.DateTimeZone; @@ -63,9 +63,9 @@ public void testSpecificToFromByteBufferWithoutLogicalTypes() throws IOException 3.14F, 3019.34, null, - new TimeConversions.DateConversion().toInt(LocalDate.now(), null, null), - new TimeConversions.TimeConversion().toInt(LocalTime.now(), null, null), - new TimeConversions.TimestampConversion().toLong( + new JodaTimeConversions.DateConversion().toInt(LocalDate.now(), null, null), + new JodaTimeConversions.TimeConversion().toInt(LocalTime.now(), null, null), + new JodaTimeConversions.TimestampConversion().toLong( DateTime.now().withZone(DateTimeZone.UTC), null, null), new Conversions.DecimalConversion().toBytes( new BigDecimal("123.45"), null, LogicalTypes.decimal(9, 2)) @@ -86,9 +86,9 @@ public void testSpecificByteArrayIncompatibleWithLogicalTypes() throws IOExcepti 3.14F, 3019.34, null, - new TimeConversions.DateConversion().toInt(LocalDate.now(), null, null), - new TimeConversions.TimeConversion().toInt(LocalTime.now(), null, null), - new TimeConversions.TimestampConversion().toLong( + new JodaTimeConversions.DateConversion().toInt(LocalDate.now(), null, null), + new JodaTimeConversions.TimeConversion().toInt(LocalTime.now(), null, null), + new JodaTimeConversions.TimestampConversion().toLong( DateTime.now().withZone(DateTimeZone.UTC), null, null), new Conversions.DecimalConversion().toBytes( new BigDecimal("123.45"), null, LogicalTypes.decimal(9, 2)) diff --git a/lang/java/avro/src/test/resources/record_with_logical_types.avsc b/lang/java/avro/src/test/resources/record_with_logical_types.avsc index 9932f95056f..f5d212917f4 100644 --- a/lang/java/avro/src/test/resources/record_with_logical_types.avsc +++ b/lang/java/avro/src/test/resources/record_with_logical_types.avsc @@ -1,7 +1,7 @@ { "type" : "record", "name" : "TestRecordWithLogicalTypes", - "doc" : "Schema for TestRecordWithLogicalTypes and TestRecordWithoutLogicalTypes, see TestSpecificLogicalTypes" + "doc" : "Schema for TestRecordWithLogicalTypes and TestRecordWithoutLogicalTypes, see TestSpecificLogicalTypes", "namespace" : "org.apache.avro.specific", "fields" : [ { "name" : "b", diff --git a/lang/java/compiler/src/main/java/org/apache/avro/compiler/specific/ProtocolTask.java b/lang/java/compiler/src/main/java/org/apache/avro/compiler/specific/ProtocolTask.java index 36bf67b51c8..71762434b1b 100644 --- a/lang/java/compiler/src/main/java/org/apache/avro/compiler/specific/ProtocolTask.java +++ b/lang/java/compiler/src/main/java/org/apache/avro/compiler/specific/ProtocolTask.java @@ -23,6 +23,7 @@ import org.apache.avro.AvroRuntimeException; import org.apache.avro.Protocol; +import org.apache.avro.compiler.specific.SpecificCompiler.DateTimeLogicalTypeType; import org.apache.avro.generic.GenericData.StringType; import org.apache.tools.ant.BuildException; @@ -36,6 +37,7 @@ public class ProtocolTask extends Task { private File src; private File dest = new File("."); private StringType stringType = StringType.CharSequence; + private DateTimeLogicalTypeType dateTimeLogicalTypeType = DateTimeLogicalTypeType.JODA; private final ArrayList filesets = new ArrayList(); @@ -51,6 +53,16 @@ public class ProtocolTask extends Task { /** Get the string type. */ public StringType getStringType() { return this.stringType; } + /** Sets the date/time logical type type (either JODA or JAVA8) */ + public void setDateTimeLogicalTypeType(DateTimeLogicalTypeType dateTimeLogicalTypeType) { + this.dateTimeLogicalTypeType = dateTimeLogicalTypeType; + } + + /** Get the date/time logical type type (either JODA or JAVA8) */ + public DateTimeLogicalTypeType getDateTimeLogicalTypeType() { + return dateTimeLogicalTypeType; + } + /** Add a fileset. */ public void addFileset(FileSet set) { filesets.add(set); } @@ -77,7 +89,7 @@ public void execute() { protected void doCompile(File src, File dir) throws IOException { Protocol protocol = Protocol.parse(src); - SpecificCompiler compiler = new SpecificCompiler(protocol); + SpecificCompiler compiler = new SpecificCompiler(protocol, getDateTimeLogicalTypeType()); compiler.setStringType(getStringType()); compiler.compileToDestination(src, dest); } diff --git a/lang/java/compiler/src/main/java/org/apache/avro/compiler/specific/SchemaTask.java b/lang/java/compiler/src/main/java/org/apache/avro/compiler/specific/SchemaTask.java index 9c5c12aa4f9..a48a8aa459b 100644 --- a/lang/java/compiler/src/main/java/org/apache/avro/compiler/specific/SchemaTask.java +++ b/lang/java/compiler/src/main/java/org/apache/avro/compiler/specific/SchemaTask.java @@ -28,7 +28,7 @@ public class SchemaTask extends ProtocolTask { protected void doCompile(File src, File dest) throws IOException { Schema.Parser parser = new Schema.Parser(); Schema schema = parser.parse(src); - SpecificCompiler compiler = new SpecificCompiler(schema); + SpecificCompiler compiler = new SpecificCompiler(schema, getDateTimeLogicalTypeType()); compiler.setStringType(getStringType()); compiler.compileToDestination(src, dest); } diff --git a/lang/java/compiler/src/main/java/org/apache/avro/compiler/specific/SpecificCompiler.java b/lang/java/compiler/src/main/java/org/apache/avro/compiler/specific/SpecificCompiler.java index 7a6f5f1cc29..e7c41f202cd 100644 --- a/lang/java/compiler/src/main/java/org/apache/avro/compiler/specific/SpecificCompiler.java +++ b/lang/java/compiler/src/main/java/org/apache/avro/compiler/specific/SpecificCompiler.java @@ -37,9 +37,8 @@ import org.apache.avro.Conversion; import org.apache.avro.Conversions; import org.apache.avro.LogicalTypes; -import org.apache.avro.data.TimeConversions.DateConversion; -import org.apache.avro.data.TimeConversions.TimeConversion; -import org.apache.avro.data.TimeConversions.TimestampConversion; +import org.apache.avro.data.Java8TimeConversions; +import org.apache.avro.data.JodaTimeConversions; import org.apache.avro.specific.SpecificData; import org.codehaus.jackson.JsonNode; @@ -88,18 +87,16 @@ public class SpecificCompiler { */ protected static final int MAX_FIELD_PARAMETER_UNIT_COUNT = JVM_METHOD_ARG_LIMIT - 1; - public static enum FieldVisibility { + public enum FieldVisibility { PUBLIC, PUBLIC_DEPRECATED, PRIVATE } - private static final SpecificData SPECIFIC = new SpecificData(); - static { - SPECIFIC.addLogicalTypeConversion(new DateConversion()); - SPECIFIC.addLogicalTypeConversion(new TimeConversion()); - SPECIFIC.addLogicalTypeConversion(new TimestampConversion()); - SPECIFIC.addLogicalTypeConversion(new Conversions.DecimalConversion()); + public enum DateTimeLogicalTypeType { + JODA, JAVA8 } + private final SpecificData specificData = new SpecificData(); + private final Set queue = new HashSet(); private Protocol protocol; private VelocityEngine velocityEngine; @@ -109,6 +106,7 @@ public static enum FieldVisibility { private boolean createAllArgsConstructor = true; private String outputCharacterEncoding; private boolean enableDecimalLogicalType = false; + private DateTimeLogicalTypeType dateTimeLogicalTypeType = DateTimeLogicalTypeType.JODA; private String suffix = ".java"; /* @@ -144,7 +142,11 @@ public boolean isCreateAllArgsConstructor() { " */\n"; public SpecificCompiler(Protocol protocol) { - this(); + this(protocol, DateTimeLogicalTypeType.JODA); + } + + public SpecificCompiler(Protocol protocol, DateTimeLogicalTypeType dateTimeLogicalTypeType) { + this(dateTimeLogicalTypeType); // enqueue all types for (Schema s : protocol.getTypes()) { enqueue(s); @@ -153,16 +155,38 @@ public SpecificCompiler(Protocol protocol) { } public SpecificCompiler(Schema schema) { - this(); + this(schema, DateTimeLogicalTypeType.JODA); + } + + public SpecificCompiler(Schema schema, DateTimeLogicalTypeType dateTimeLogicalTypeType) { + this(dateTimeLogicalTypeType); enqueue(schema); this.protocol = null; } + /** + * Creates a specific compler with the default (Joda) type for date/time related logical types. + * + * @see #SpecificCompiler(DateTimeLogicalTypeType) + */ SpecificCompiler() { + this(DateTimeLogicalTypeType.JODA); + } + + /** + * Creates a specific compiler with the given type to use for date/time related logical types. + * Use {@link DateTimeLogicalTypeType#JODA} to generate Joda Time classes, use {@link DateTimeLogicalTypeType#JAVA8} + * to generate {@code java.time.*} classes for the date/time local types. + * + * @param dateTimeLogicalTypeType the types used for date/time related logical types + */ + SpecificCompiler(DateTimeLogicalTypeType dateTimeLogicalTypeType) { + this.dateTimeLogicalTypeType = dateTimeLogicalTypeType; this.templateDir = System.getProperty("org.apache.avro.specific.templates", "/org/apache/avro/compiler/specific/templates/java/classic/"); initializeVelocity(); + initializeSpecificData(); } /** Set the resource directory where templates reside. First, the compiler checks @@ -225,6 +249,14 @@ public void setEnableDecimalLogicalType(boolean enableDecimalLogicalType) { this.enableDecimalLogicalType = enableDecimalLogicalType; } + public boolean useJodaForDateTimeLogicalTypes() { + return dateTimeLogicalTypeType == DateTimeLogicalTypeType.JODA; + } + + public boolean useJava8ForDateTimeLogicalTypes() { + return dateTimeLogicalTypeType == DateTimeLogicalTypeType.JAVA8; + } + private static String logChuteName = null; private void initializeVelocity() { @@ -253,6 +285,20 @@ private void initializeVelocity() { velocityEngine.setProperty("runtime.log.logsystem.class", logChuteName); } + private void initializeSpecificData() { + if(dateTimeLogicalTypeType == DateTimeLogicalTypeType.JODA) { + specificData.addLogicalTypeConversion(new JodaTimeConversions.DateConversion()); + specificData.addLogicalTypeConversion(new JodaTimeConversions.TimeConversion()); + specificData.addLogicalTypeConversion(new JodaTimeConversions.TimestampConversion()); + } else if (dateTimeLogicalTypeType == DateTimeLogicalTypeType.JAVA8) { + specificData.addLogicalTypeConversion(new Java8TimeConversions.DateConversion()); + specificData.addLogicalTypeConversion(new Java8TimeConversions.TimeMillisConversion()); + specificData.addLogicalTypeConversion(new Java8TimeConversions.TimestampMillisConversion()); + } + + specificData.addLogicalTypeConversion(new Conversions.DecimalConversion()); + } + /** * Captures output file path and contents. */ @@ -324,7 +370,7 @@ public static void compileSchema(File[] srcFiles, File dest) throws IOException for (File src : srcFiles) { Schema schema = parser.parse(src); - SpecificCompiler compiler = new SpecificCompiler(schema); + SpecificCompiler compiler = new SpecificCompiler(schema, DateTimeLogicalTypeType.JODA); compiler.compileToDestination(src, dest); } } @@ -630,7 +676,7 @@ private String javaType(Schema schema, boolean checkConvertedLogicalType) { private String getConvertedLogicalType(Schema schema) { if (enableDecimalLogicalType || !(schema.getLogicalType() instanceof LogicalTypes.Decimal)) { - Conversion conversion = SPECIFIC + Conversion conversion = specificData .getConversionFor(schema.getLogicalType()); if (conversion != null) { return conversion.getConvertedType().getName(); diff --git a/lang/java/compiler/src/main/velocity/org/apache/avro/compiler/specific/templates/java/classic/record.vm b/lang/java/compiler/src/main/velocity/org/apache/avro/compiler/specific/templates/java/classic/record.vm index ccec4b60c1d..aa566486e46 100644 --- a/lang/java/compiler/src/main/velocity/org/apache/avro/compiler/specific/templates/java/classic/record.vm +++ b/lang/java/compiler/src/main/velocity/org/apache/avro/compiler/specific/templates/java/classic/record.vm @@ -151,10 +151,16 @@ public class ${this.mangle($schema.getName())}#if ($schema.isError()) extends or } #if ($this.hasLogicalTypeField($schema)) - protected static final org.apache.avro.data.TimeConversions.DateConversion DATE_CONVERSION = new org.apache.avro.data.TimeConversions.DateConversion(); - protected static final org.apache.avro.data.TimeConversions.TimeConversion TIME_CONVERSION = new org.apache.avro.data.TimeConversions.TimeConversion(); - protected static final org.apache.avro.data.TimeConversions.TimestampConversion TIMESTAMP_CONVERSION = new org.apache.avro.data.TimeConversions.TimestampConversion(); protected static final org.apache.avro.Conversions.DecimalConversion DECIMAL_CONVERSION = new org.apache.avro.Conversions.DecimalConversion(); +#if ($this.useJodaForDateTimeLogicalTypes()) + protected static final org.apache.avro.data.JodaTimeConversions.DateConversion DATE_CONVERSION = new org.apache.avro.data.JodaTimeConversions.DateConversion(); + protected static final org.apache.avro.data.JodaTimeConversions.TimeConversion TIME_CONVERSION = new org.apache.avro.data.JodaTimeConversions.TimeConversion(); + protected static final org.apache.avro.data.JodaTimeConversions.TimestampConversion TIMESTAMP_CONVERSION = new org.apache.avro.data.JodaTimeConversions.TimestampConversion(); +#elseif ($this.useJava8ForDateTimeLogicalTypes()) + protected static final org.apache.avro.data.Java8TimeConversions.DateConversion DATE_CONVERSION = new org.apache.avro.data.Java8TimeConversions.DateConversion(); + protected static final org.apache.avro.data.Java8TimeConversions.TimeMillisConversion TIME_CONVERSION = new org.apache.avro.data.Java8TimeConversions.TimeMillisConversion(); + protected static final org.apache.avro.data.Java8TimeConversions.TimestampMillisConversion TIMESTAMP_CONVERSION = new org.apache.avro.data.Java8TimeConversions.TimestampMillisConversion(); +#end private static final org.apache.avro.Conversion[] conversions = new org.apache.avro.Conversion[] { diff --git a/lang/java/compiler/src/test/java/org/apache/avro/compiler/specific/TestSpecificCompiler.java b/lang/java/compiler/src/test/java/org/apache/avro/compiler/specific/TestSpecificCompiler.java index 35d53b5243d..08fd55c5b82 100644 --- a/lang/java/compiler/src/test/java/org/apache/avro/compiler/specific/TestSpecificCompiler.java +++ b/lang/java/compiler/src/test/java/org/apache/avro/compiler/specific/TestSpecificCompiler.java @@ -17,6 +17,8 @@ */ package org.apache.avro.compiler.specific; +import static org.apache.avro.compiler.specific.SpecificCompiler.DateTimeLogicalTypeType.JODA; +import static org.apache.avro.compiler.specific.SpecificCompiler.DateTimeLogicalTypeType.JAVA8; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -39,6 +41,7 @@ import org.apache.avro.LogicalTypes; import org.apache.avro.Schema; import org.apache.avro.SchemaBuilder; +import org.apache.avro.compiler.specific.SpecificCompiler.DateTimeLogicalTypeType; import org.apache.avro.generic.GenericData.StringType; import org.junit.After; import org.junit.Assert; @@ -112,9 +115,13 @@ private static Schema createSampleRecordSchema(int numStringFields, int numDoubl } private SpecificCompiler createCompiler() throws IOException { + return createCompiler(JODA); + } + + private SpecificCompiler createCompiler(DateTimeLogicalTypeType dateTimeLogicalTypeType) throws IOException { Schema.Parser parser = new Schema.Parser(); Schema schema = parser.parse(this.src); - SpecificCompiler compiler = new SpecificCompiler(schema); + SpecificCompiler compiler = new SpecificCompiler(schema, dateTimeLogicalTypeType); compiler.setTemplateDir(this.velocityTemplateDir); compiler.setStringType(StringType.CharSequence); return compiler; @@ -377,6 +384,26 @@ public void testJavaTypeWithDecimalLogicalTypeDisabled() throws Exception { "java.lang.CharSequence", compiler.javaType(uuidSchema)); } + @Test + public void testJavaTypeWithJava8DateTimeTypes() throws Exception { + SpecificCompiler compiler = createCompiler(JAVA8); + + Schema dateSchema = LogicalTypes.date() + .addToSchema(Schema.create(Schema.Type.INT)); + Schema timeSchema = LogicalTypes.timeMillis() + .addToSchema(Schema.create(Schema.Type.INT)); + Schema timestampSchema = LogicalTypes.timestampMillis() + .addToSchema(Schema.create(Schema.Type.LONG)); + + // Date/time types should always use upper level java classes + Assert.assertEquals("Should use java.time.LocalDate for date type", + "java.time.LocalDate", compiler.javaType(dateSchema)); + Assert.assertEquals("Should use java.time.LocalTime for time-millis type", + "java.time.LocalTime", compiler.javaType(timeSchema)); + Assert.assertEquals("Should use java.time.Instant for timestamp-millis type", + "java.time.Instant", compiler.javaType(timestampSchema)); + } + @Test public void testJavaUnbox() throws Exception { SpecificCompiler compiler = createCompiler(); @@ -412,7 +439,26 @@ public void testJavaUnbox() throws Exception { "org.joda.time.LocalTime", compiler.javaUnbox(timeSchema)); Assert.assertEquals("Should use Joda DateTime for timestamp-millis type", "org.joda.time.DateTime", compiler.javaUnbox(timestampSchema)); + } + @Test + public void testJavaUnboxJava8DateTime() throws Exception { + SpecificCompiler compiler = createCompiler(JAVA8); + + Schema dateSchema = LogicalTypes.date() + .addToSchema(Schema.create(Schema.Type.INT)); + Schema timeSchema = LogicalTypes.timeMillis() + .addToSchema(Schema.create(Schema.Type.INT)); + Schema timestampSchema = LogicalTypes.timestampMillis() + .addToSchema(Schema.create(Schema.Type.LONG)); + // Date/time types should always use upper level java classes, even though + // their underlying representations are primitive types + Assert.assertEquals("Should use java.time.LocalDate for date type", + "java.time.LocalDate", compiler.javaUnbox(dateSchema)); + Assert.assertEquals("Should use java.time.LocalTime for time-millis type", + "java.time.LocalTime", compiler.javaUnbox(timeSchema)); + Assert.assertEquals("Should use java.time.Instant for timestamp-millis type", + "java.time.Instant", compiler.javaUnbox(timestampSchema)); } @Test @@ -475,6 +521,14 @@ public void testLogicalTypesWithMultipleFields() throws Exception { new SpecificCompiler(logicalTypesWithMultipleFields).compile()); } + @Test + public void testLogicalTypesWithMultipleFieldsJava8DateTime() throws Exception { + Schema logicalTypesWithMultipleFields = new Schema.Parser().parse( + new File("src/test/resources/logical_types_with_multiple_fields.avsc")); + assertCompilesWithJavaCompiler( + new SpecificCompiler(logicalTypesWithMultipleFields, JAVA8).compile()); + } + @Test public void testConversionInstanceWithDecimalLogicalTypeDisabled() throws Exception { SpecificCompiler compiler = createCompiler(); diff --git a/lang/java/maven-plugin/src/main/java/org/apache/avro/mojo/AbstractAvroMojo.java b/lang/java/maven-plugin/src/main/java/org/apache/avro/mojo/AbstractAvroMojo.java index 9d78ede9133..69edf3c0936 100644 --- a/lang/java/maven-plugin/src/main/java/org/apache/avro/mojo/AbstractAvroMojo.java +++ b/lang/java/maven-plugin/src/main/java/org/apache/avro/mojo/AbstractAvroMojo.java @@ -129,6 +129,14 @@ public abstract class AbstractAvroMojo extends AbstractMojo { */ protected boolean enableDecimalLogicalType; + /** + * Determines which type of classes to generate for date/time related logical types. Either 'joda' or 'java8'. + * Defaults to joda for backwards compatibility reasons. + * + * @parameter default-value="joda" + */ + protected String dateTimeLogicalTypeType = SpecificCompiler.DateTimeLogicalTypeType.JODA.name().toLowerCase(); + /** * The current Maven project. * @@ -237,6 +245,15 @@ protected SpecificCompiler.FieldVisibility getFieldVisibility() { } } + protected SpecificCompiler.DateTimeLogicalTypeType getDateTimeLocalTypeType() { + try { + String upper = String.valueOf(this.dateTimeLogicalTypeType).trim().toUpperCase(); + return SpecificCompiler.DateTimeLogicalTypeType.valueOf(upper); + } catch (IllegalArgumentException e) { + return SpecificCompiler.DateTimeLogicalTypeType.JODA; + } + } + protected abstract void doCompile(String filename, File sourceDirectory, File outputDirectory) throws IOException; protected abstract String[] getIncludes(); diff --git a/lang/java/maven-plugin/src/main/java/org/apache/avro/mojo/IDLProtocolMojo.java b/lang/java/maven-plugin/src/main/java/org/apache/avro/mojo/IDLProtocolMojo.java index 3f61ac2a2b0..f82b30ad315 100644 --- a/lang/java/maven-plugin/src/main/java/org/apache/avro/mojo/IDLProtocolMojo.java +++ b/lang/java/maven-plugin/src/main/java/org/apache/avro/mojo/IDLProtocolMojo.java @@ -88,7 +88,7 @@ protected void doCompile(String filename, File sourceDirectory, File outputDirec Protocol p = parser.CompilationUnit(); String json = p.toString(true); Protocol protocol = Protocol.parse(json); - SpecificCompiler compiler = new SpecificCompiler(protocol); + SpecificCompiler compiler = new SpecificCompiler(protocol, getDateTimeLocalTypeType()); compiler.setStringType(GenericData.StringType.valueOf(stringType)); compiler.setTemplateDir(templateDirectory); compiler.setFieldVisibility(getFieldVisibility()); diff --git a/lang/java/maven-plugin/src/main/java/org/apache/avro/mojo/ProtocolMojo.java b/lang/java/maven-plugin/src/main/java/org/apache/avro/mojo/ProtocolMojo.java index a30a08245b9..a2f898c9850 100644 --- a/lang/java/maven-plugin/src/main/java/org/apache/avro/mojo/ProtocolMojo.java +++ b/lang/java/maven-plugin/src/main/java/org/apache/avro/mojo/ProtocolMojo.java @@ -56,7 +56,7 @@ public class ProtocolMojo extends AbstractAvroMojo { protected void doCompile(String filename, File sourceDirectory, File outputDirectory) throws IOException { File src = new File(sourceDirectory, filename); Protocol protocol = Protocol.parse(src); - SpecificCompiler compiler = new SpecificCompiler(protocol); + SpecificCompiler compiler = new SpecificCompiler(protocol, getDateTimeLocalTypeType()); compiler.setTemplateDir(templateDirectory); compiler.setStringType(StringType.valueOf(stringType)); compiler.setFieldVisibility(getFieldVisibility()); diff --git a/lang/java/maven-plugin/src/main/java/org/apache/avro/mojo/SchemaMojo.java b/lang/java/maven-plugin/src/main/java/org/apache/avro/mojo/SchemaMojo.java index 9a849207f33..48b73f397f0 100644 --- a/lang/java/maven-plugin/src/main/java/org/apache/avro/mojo/SchemaMojo.java +++ b/lang/java/maven-plugin/src/main/java/org/apache/avro/mojo/SchemaMojo.java @@ -73,7 +73,7 @@ protected void doCompile(String filename, File sourceDirectory, File outputDirec schema = schemaParser.parse(src); } - SpecificCompiler compiler = new SpecificCompiler(schema); + SpecificCompiler compiler = new SpecificCompiler(schema, getDateTimeLocalTypeType()); compiler.setTemplateDir(templateDirectory); compiler.setStringType(StringType.valueOf(stringType)); compiler.setFieldVisibility(getFieldVisibility()); diff --git a/lang/java/maven-plugin/src/test/avro/User.avdl b/lang/java/maven-plugin/src/test/avro/User.avdl index 4d4fa5a9a1f..cb63ee52848 100644 --- a/lang/java/maven-plugin/src/test/avro/User.avdl +++ b/lang/java/maven-plugin/src/test/avro/User.avdl @@ -25,6 +25,7 @@ protocol IdlTest { record IdlUser { union { null, string } id; union { null, long } createdOn; + timestamp_ms modifiedOn; union { null, IdlPrivacy } privacy; } diff --git a/lang/java/maven-plugin/src/test/avro/User.avpr b/lang/java/maven-plugin/src/test/avro/User.avpr index 9d8a10958d0..6dd8b9b8900 100644 --- a/lang/java/maven-plugin/src/test/avro/User.avpr +++ b/lang/java/maven-plugin/src/test/avro/User.avpr @@ -27,6 +27,13 @@ "name": "privacy", "type": ["null", "ProtocolPrivacy"], "default": null + }, + { + "name": "modifiedOn", + "type": { + "type": "long", + "logicalType": "timestamp-millis" + } } ] } diff --git a/lang/java/maven-plugin/src/test/avro/User.avsc b/lang/java/maven-plugin/src/test/avro/User.avsc index 0eb5e096ee6..a93e0d13f21 100644 --- a/lang/java/maven-plugin/src/test/avro/User.avsc +++ b/lang/java/maven-plugin/src/test/avro/User.avsc @@ -33,6 +33,13 @@ "name": "privacyDirectImport", "type": ["null", "test.PrivacyDirectImport"], "default": null + }, + { + "name": "time", + "type": { + "type": "long", + "logicalType": "timestamp-millis" + } } ] } diff --git a/lang/java/maven-plugin/src/test/java/org/apache/avro/mojo/AbstractAvroMojoTest.java b/lang/java/maven-plugin/src/test/java/org/apache/avro/mojo/AbstractAvroMojoTest.java index 23487814ea9..24e79c0647f 100644 --- a/lang/java/maven-plugin/src/test/java/org/apache/avro/mojo/AbstractAvroMojoTest.java +++ b/lang/java/maven-plugin/src/test/java/org/apache/avro/mojo/AbstractAvroMojoTest.java @@ -20,14 +20,18 @@ import java.io.File; import java.util.Arrays; import java.util.List; + import org.apache.maven.plugin.testing.AbstractMojoTestCase; +import static java.util.Arrays.stream; +import static java.util.stream.Collectors.toList; + /** * Base class for all Arvo mojo test classes. * * @author saden */ -public class AbstractAvroMojoTest extends AbstractMojoTestCase { +public abstract class AbstractAvroMojoTest extends AbstractMojoTestCase { @Override protected void setUp() throws Exception { diff --git a/lang/java/maven-plugin/src/test/java/org/apache/avro/mojo/TestIDLProtocolMojo.java b/lang/java/maven-plugin/src/test/java/org/apache/avro/mojo/TestIDLProtocolMojo.java index 9316872849e..20cb3601119 100644 --- a/lang/java/maven-plugin/src/test/java/org/apache/avro/mojo/TestIDLProtocolMojo.java +++ b/lang/java/maven-plugin/src/test/java/org/apache/avro/mojo/TestIDLProtocolMojo.java @@ -17,7 +17,13 @@ */ package org.apache.avro.mojo; +import org.codehaus.plexus.util.FileUtils; + import java.io.File; +import java.io.FileReader; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.List; /** * Test the IDL Protocol Mojo. @@ -26,19 +32,36 @@ */ public class TestIDLProtocolMojo extends AbstractAvroMojoTest { - protected File testPom = new File(getBasedir(), - "src/test/resources/unit/idl/pom.xml"); + protected File jodaTestPom = new File(getBasedir(), + "src/test/resources/unit/idl/pom-joda.xml"); + protected File java8TestPom = new File(getBasedir(), + "src/test/resources/unit/idl/pom-java8.xml"); + + public void testIdlProtocolMojoJoda() throws Exception { + IDLProtocolMojo mojo = (IDLProtocolMojo) lookupMojo("idl-protocol", jodaTestPom); + + assertNotNull(mojo); + mojo.execute(); + + File outputDir = new File(getBasedir(), "target/test-harness/idl-joda/test"); + String[] generatedFileNames = new String[]{"IdlPrivacy.java", + "IdlTest.java", "IdlUser.java", "IdlUserWrapper.java"}; + + String idlUserContent = FileUtils.fileRead(new File(outputDir, "IdlUser.java")); + assertTrue(idlUserContent.contains("org.joda.time.DateTime")); + } - public void testIdlProtocolMojo() throws Exception { - IDLProtocolMojo mojo = (IDLProtocolMojo) lookupMojo("idl-protocol", testPom); + public void testIdlProtocolMojoJava8() throws Exception { + IDLProtocolMojo mojo = (IDLProtocolMojo) lookupMojo("idl-protocol", java8TestPom); assertNotNull(mojo); mojo.execute(); - File outputDir = new File(getBasedir(), "target/test-harness/idl/test"); - String[] generatedFiles = new String[]{"IdlPrivacy.java", + File outputDir = new File(getBasedir(), "target/test-harness/idl-java8/test"); + String[] generatedFileNames = new String[]{"IdlPrivacy.java", "IdlTest.java", "IdlUser.java", "IdlUserWrapper.java"}; - assertFilesExist(outputDir, generatedFiles); + String idlUserContent = FileUtils.fileRead(new File(outputDir, "IdlUser.java")); + assertTrue(idlUserContent.contains("java.time.Instant")); } } diff --git a/lang/java/maven-plugin/src/test/java/org/apache/avro/mojo/TestProtocolMojo.java b/lang/java/maven-plugin/src/test/java/org/apache/avro/mojo/TestProtocolMojo.java index 342c155f431..7f5890d02ce 100644 --- a/lang/java/maven-plugin/src/test/java/org/apache/avro/mojo/TestProtocolMojo.java +++ b/lang/java/maven-plugin/src/test/java/org/apache/avro/mojo/TestProtocolMojo.java @@ -17,6 +17,8 @@ */ package org.apache.avro.mojo; +import org.codehaus.plexus.util.FileUtils; + import java.io.File; /** @@ -26,19 +28,40 @@ */ public class TestProtocolMojo extends AbstractAvroMojoTest { - protected File testPom = new File(getBasedir(), - "src/test/resources/unit/protocol/pom.xml"); + protected File jodaTestPom = new File(getBasedir(), + "src/test/resources/unit/protocol/pom-joda.xml"); + protected File java8TestPom = new File(getBasedir(), + "src/test/resources/unit/protocol/pom-java8.xml"); - public void testProtocolMojo() throws Exception { - ProtocolMojo mojo = (ProtocolMojo) lookupMojo("protocol", testPom); + public void testProtocolMojoJoda() throws Exception { + ProtocolMojo mojo = (ProtocolMojo) lookupMojo("protocol", jodaTestPom); assertNotNull(mojo); mojo.execute(); - File outputDir = new File(getBasedir(), "target/test-harness/protocol/test"); + File outputDir = new File(getBasedir(), "target/test-harness/protocol-joda/test"); String[] generatedFiles = new String[]{"ProtocolPrivacy.java", "ProtocolTest.java", "ProtocolUser.java"}; assertFilesExist(outputDir, generatedFiles); + + String protocolUserContent = FileUtils.fileRead(new File(outputDir, "ProtocolUser.java")); + assertTrue(protocolUserContent.contains("org.joda.time.DateTime")); + } + + public void testProtocolMojoJava8() throws Exception { + ProtocolMojo mojo = (ProtocolMojo) lookupMojo("protocol", java8TestPom); + + assertNotNull(mojo); + mojo.execute(); + + File outputDir = new File(getBasedir(), "target/test-harness/protocol-java8/test"); + String[] generatedFiles = new String[]{"ProtocolPrivacy.java", + "ProtocolTest.java", "ProtocolUser.java"}; + + assertFilesExist(outputDir, generatedFiles); + + String protocolUserContent = FileUtils.fileRead(new File(outputDir, "ProtocolUser.java")); + assertTrue(protocolUserContent.contains("java.time.Instant")); } } diff --git a/lang/java/maven-plugin/src/test/java/org/apache/avro/mojo/TestSchemaMojo.java b/lang/java/maven-plugin/src/test/java/org/apache/avro/mojo/TestSchemaMojo.java index 3e7fe745494..fb2ab91030f 100644 --- a/lang/java/maven-plugin/src/test/java/org/apache/avro/mojo/TestSchemaMojo.java +++ b/lang/java/maven-plugin/src/test/java/org/apache/avro/mojo/TestSchemaMojo.java @@ -17,6 +17,8 @@ */ package org.apache.avro.mojo; +import org.codehaus.plexus.util.FileUtils; + import java.io.File; /** @@ -26,19 +28,40 @@ */ public class TestSchemaMojo extends AbstractAvroMojoTest { - protected File testPom = new File(getBasedir(), - "src/test/resources/unit/schema/pom.xml"); + protected File jodaTestPom = new File(getBasedir(), + "src/test/resources/unit/schema/pom-joda.xml"); + protected File java8TestPom = new File(getBasedir(), + "src/test/resources/unit/schema/pom-java8.xml"); - public void testSchemaMojo() throws Exception { - SchemaMojo mojo = (SchemaMojo) lookupMojo("schema", testPom); + public void testSchemaMojoJoda() throws Exception { + SchemaMojo mojo = (SchemaMojo) lookupMojo("schema", jodaTestPom); assertNotNull(mojo); mojo.execute(); - File outputDir = new File(getBasedir(), "target/test-harness/schema/test"); + File outputDir = new File(getBasedir(), "target/test-harness/schema-joda/test"); String[] generatedFiles = new String[]{"PrivacyDirectImport.java", "PrivacyImport.java", "SchemaPrivacy.java", "SchemaUser.java"}; assertFilesExist(outputDir, generatedFiles); + + String schemaUserContent = FileUtils.fileRead(new File(outputDir, "SchemaUser.java")); + assertTrue(schemaUserContent.contains("org.joda.time.DateTime")); + } + + public void testSchemaMojoJava8() throws Exception { + SchemaMojo mojo = (SchemaMojo) lookupMojo("schema", java8TestPom); + + assertNotNull(mojo); + mojo.execute(); + + File outputDir = new File(getBasedir(), "target/test-harness/schema-java8/test"); + String[] generatedFiles = new String[]{"PrivacyDirectImport.java", + "PrivacyImport.java", "SchemaPrivacy.java", "SchemaUser.java"}; + + assertFilesExist(outputDir, generatedFiles); + + String schemaUserContent = FileUtils.fileRead(new File(outputDir, "SchemaUser.java")); + assertTrue(schemaUserContent.contains("java.time.Instant")); } } diff --git a/lang/java/maven-plugin/src/test/resources/unit/idl/pom-java8.xml b/lang/java/maven-plugin/src/test/resources/unit/idl/pom-java8.xml new file mode 100644 index 00000000000..099a7e7a71c --- /dev/null +++ b/lang/java/maven-plugin/src/test/resources/unit/idl/pom-java8.xml @@ -0,0 +1,67 @@ + + + + 4.0.0 + + avro-parent + org.apache.avro + 1.7.3-SNAPSHOT + ../../../../../../../../../ + + avro-maven-plugin-test + jar + + testproject + + + + + avro-maven-plugin + + + idl + + idl-protocol + + + + + ${basedir}/src/test + ${basedir}/target/test-harness/idl-java8 + String + java8 + + + + + + + + org.apache.avro + avro + 1.7.3-SNAPSHOT + + + org.codehaus.jackson + jackson-mapper-asl + 1.9.10 + + + + diff --git a/lang/java/maven-plugin/src/test/resources/unit/idl/pom.xml b/lang/java/maven-plugin/src/test/resources/unit/idl/pom-joda.xml similarity index 92% rename from lang/java/maven-plugin/src/test/resources/unit/idl/pom.xml rename to lang/java/maven-plugin/src/test/resources/unit/idl/pom-joda.xml index 69c504bb35e..f5347a649ab 100644 --- a/lang/java/maven-plugin/src/test/resources/unit/idl/pom.xml +++ b/lang/java/maven-plugin/src/test/resources/unit/idl/pom-joda.xml @@ -43,7 +43,9 @@ ${basedir}/src/test - ${basedir}/target/test-harness/idl + ${basedir}/target/test-harness/idl-joda + String + joda diff --git a/lang/java/maven-plugin/src/test/resources/unit/protocol/pom-java8.xml b/lang/java/maven-plugin/src/test/resources/unit/protocol/pom-java8.xml new file mode 100644 index 00000000000..6f1db51d606 --- /dev/null +++ b/lang/java/maven-plugin/src/test/resources/unit/protocol/pom-java8.xml @@ -0,0 +1,66 @@ + + + + 4.0.0 + + avro-parent + org.apache.avro + 1.7.3-SNAPSHOT + ../../../../../../../../../ + + avro-maven-plugin-test + jar + + testproject + + + + + avro-maven-plugin + + + protocol + + protocol + + + + + ${basedir}/src/test/avro + ${basedir}/target/test-harness/protocol-java8 + java8 + + + + + + + + org.apache.avro + avro + 1.7.3-SNAPSHOT + + + org.codehaus.jackson + jackson-mapper-asl + 1.9.10 + + + + diff --git a/lang/java/maven-plugin/src/test/resources/unit/protocol/pom.xml b/lang/java/maven-plugin/src/test/resources/unit/protocol/pom-joda.xml similarity index 98% rename from lang/java/maven-plugin/src/test/resources/unit/protocol/pom.xml rename to lang/java/maven-plugin/src/test/resources/unit/protocol/pom-joda.xml index b484e3dd47a..d01cafa9b19 100644 --- a/lang/java/maven-plugin/src/test/resources/unit/protocol/pom.xml +++ b/lang/java/maven-plugin/src/test/resources/unit/protocol/pom-joda.xml @@ -43,7 +43,7 @@ ${basedir}/src/test/avro - ${basedir}/target/test-harness/protocol + ${basedir}/target/test-harness/protocol-joda diff --git a/lang/java/maven-plugin/src/test/resources/unit/schema/pom-java8.xml b/lang/java/maven-plugin/src/test/resources/unit/schema/pom-java8.xml new file mode 100644 index 00000000000..a43188382fc --- /dev/null +++ b/lang/java/maven-plugin/src/test/resources/unit/schema/pom-java8.xml @@ -0,0 +1,65 @@ + + + + 4.0.0 + + avro-maven-plugin-test + jar + + testproject + + + + + avro-maven-plugin + + + schema + + schema + + + + + ${basedir}/src/test/avro + ${basedir}/target/test-harness/schema-java8 + java8 + + ${basedir}/src/test/avro/imports + ${basedir}/src/test/avro/directImport/PrivacyDirectImport.avsc + + + + + + + + + org.apache.avro + avro + 1.7.3-SNAPSHOT + + + org.codehaus.jackson + jackson-mapper-asl + 1.9.10 + + + + diff --git a/lang/java/maven-plugin/src/test/resources/unit/schema/pom.xml b/lang/java/maven-plugin/src/test/resources/unit/schema/pom-joda.xml similarity index 98% rename from lang/java/maven-plugin/src/test/resources/unit/schema/pom.xml rename to lang/java/maven-plugin/src/test/resources/unit/schema/pom-joda.xml index cc000df05ec..396da470e57 100644 --- a/lang/java/maven-plugin/src/test/resources/unit/schema/pom.xml +++ b/lang/java/maven-plugin/src/test/resources/unit/schema/pom-joda.xml @@ -38,7 +38,7 @@ ${basedir}/src/test/avro - ${basedir}/target/test-harness/schema + ${basedir}/target/test-harness/schema-joda ${basedir}/src/test/avro/imports ${basedir}/src/test/avro/directImport/PrivacyDirectImport.avsc diff --git a/lang/java/tools/src/main/java/org/apache/avro/tool/SpecificCompilerTool.java b/lang/java/tools/src/main/java/org/apache/avro/tool/SpecificCompilerTool.java index 2ba6616e12b..b0f335670e3 100644 --- a/lang/java/tools/src/main/java/org/apache/avro/tool/SpecificCompilerTool.java +++ b/lang/java/tools/src/main/java/org/apache/avro/tool/SpecificCompilerTool.java @@ -23,12 +23,15 @@ import java.io.InputStream; import java.io.PrintStream; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Optional; import java.util.Set; import java.util.LinkedHashSet; import java.util.List; import org.apache.avro.Protocol; import org.apache.avro.Schema; +import org.apache.avro.compiler.specific.SpecificCompiler.DateTimeLogicalTypeType; import org.apache.avro.generic.GenericData.StringType; import org.apache.avro.compiler.specific.SpecificCompiler; @@ -43,7 +46,7 @@ public int run(InputStream in, PrintStream out, PrintStream err, List args) throws Exception { if (args.size() < 3) { System.err - .println("Usage: [-encoding ] [-string] [-bigDecimal] (schema|protocol) input... outputdir"); + .println("Usage: [-encoding ] [-string] [-bigDecimal] [-dateTimeLogicalType ] (schema|protocol) input... outputdir"); System.err .println(" input - input files or directories"); System.err @@ -53,18 +56,21 @@ public int run(InputStream in, PrintStream out, PrintStream err, System.err.println(" -string - use java.lang.String instead of Utf8"); System.err.println(" -bigDecimal - use java.math.BigDecimal for " + "decimal type instead of java.nio.ByteBuffer"); + System.err.println(" -dateTimeLogicalType [joda|java8] use either " + + "Joda time classes (default) or java 8 native date/time classes"); return 1; } StringType stringType = StringType.CharSequence; boolean useLogicalDecimal = false; + Optional dateTimeLogicalTypeType = Optional.empty(); + Optional encoding = Optional.empty(); int arg = 0; - String encoding = null; if ("-encoding".equals(args.get(arg))) { arg++; - encoding = args.get(arg); + encoding = Optional.of(args.get(arg)); arg++; } @@ -78,6 +84,17 @@ public int run(InputStream in, PrintStream out, PrintStream err, arg++; } + if ("-dateTimeLogicalType".equalsIgnoreCase(args.get(arg))) { + arg++; + try { + dateTimeLogicalTypeType = Optional.of(DateTimeLogicalTypeType.valueOf(args.get(arg).toUpperCase())); + } catch (IllegalArgumentException | IndexOutOfBoundsException e) { + System.err.println("Expected one of " + Arrays.toString(DateTimeLogicalTypeType.values())); + return 1; + } + arg++; + } + String method = args.get(arg); List inputs = new ArrayList(); File output = new File(args.get(args.size() - 1)); @@ -90,13 +107,13 @@ public int run(InputStream in, PrintStream out, PrintStream err, Schema.Parser parser = new Schema.Parser(); for (File src : determineInputs(inputs, SCHEMA_FILTER)) { Schema schema = parser.parse(src); - SpecificCompiler compiler = new SpecificCompiler(schema); + SpecificCompiler compiler = new SpecificCompiler(schema, dateTimeLogicalTypeType.orElse(DateTimeLogicalTypeType.JODA)); executeCompiler(compiler, encoding, stringType, useLogicalDecimal, src, output); } } else if ("protocol".equals(method)) { for (File src : determineInputs(inputs, PROTOCOL_FILTER)) { Protocol protocol = Protocol.parse(src); - SpecificCompiler compiler = new SpecificCompiler(protocol); + SpecificCompiler compiler = new SpecificCompiler(protocol, dateTimeLogicalTypeType.orElse(DateTimeLogicalTypeType.JODA)); executeCompiler(compiler, encoding, stringType, useLogicalDecimal, src, output); } } else { @@ -107,16 +124,14 @@ public int run(InputStream in, PrintStream out, PrintStream err, } private void executeCompiler(SpecificCompiler compiler, - String encoding, + Optional encoding, StringType stringType, boolean enableDecimalLogicalType, File src, File output) throws IOException { compiler.setStringType(stringType); compiler.setEnableDecimalLogicalType(enableDecimalLogicalType); - if (encoding != null) { - compiler.setOutputCharacterEncoding(encoding); - } + encoding.ifPresent(compiler::setOutputCharacterEncoding); compiler.compileToDestination(src, output); }