diff --git a/lang/java/avro/src/main/java/org/apache/avro/Schema.java b/lang/java/avro/src/main/java/org/apache/avro/Schema.java index 3120cfd540a..2f0711ed401 100644 --- a/lang/java/avro/src/main/java/org/apache/avro/Schema.java +++ b/lang/java/avro/src/main/java/org/apache/avro/Schema.java @@ -25,6 +25,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.DoubleNode; import com.fasterxml.jackson.databind.node.NullNode; +import org.apache.avro.path.TracingAvroTypeException; import org.apache.avro.util.internal.Accessor; import org.apache.avro.util.internal.Accessor.FieldAccessor; import org.apache.avro.util.internal.JacksonUtils; @@ -1113,7 +1114,12 @@ public boolean hasEnumSymbol(String symbol) { @Override public int getEnumOrdinal(String symbol) { - return ordinals.get(symbol); + Integer ordinal = ordinals.get(symbol); + if (ordinal == null) { + throw new TracingAvroTypeException( + new AvroTypeException("enum value '" + symbol + "' is not in the enum symbol set: " + symbols)); + } + return ordinal; } @Override diff --git a/lang/java/avro/src/test/java/org/apache/avro/TestSchema.java b/lang/java/avro/src/test/java/org/apache/avro/TestSchema.java index 6c4e35df97e..9a3b14ee754 100644 --- a/lang/java/avro/src/test/java/org/apache/avro/TestSchema.java +++ b/lang/java/avro/src/test/java/org/apache/avro/TestSchema.java @@ -45,6 +45,13 @@ import org.apache.avro.Schema.Field; import org.apache.avro.Schema.Type; import org.apache.avro.generic.GenericData; +import org.apache.avro.generic.GenericData.EnumSymbol; +import org.apache.avro.generic.GenericData.Record; +import org.apache.avro.generic.GenericDatumWriter; +import org.apache.avro.generic.GenericRecord; +import org.apache.avro.generic.GenericRecordBuilder; +import org.apache.avro.io.Encoder; +import org.apache.avro.io.EncoderFactory; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -636,4 +643,33 @@ void add_types() { void testParserNullValidate() { new Schema.Parser((NameValidator) null).parse("{\"type\":\"record\",\"name\":\"\",\"fields\":[]}"); // Empty name } + + /** + * Tests when a user tries to write a record with an invalid enum symbol value + * that the exception returned is more descriptive than just a NPE or an + * incorrect mention of an unspecified non-null field. + */ + @Test + void enumWriteUnknownField() throws IOException { + Schema schema = Schema.createRecord("record1", "doc", "", false); + String goodValue = "HELLO"; + Schema enumSchema = Schema.createEnum("enum1", "doc", "", Arrays.asList(goodValue)); + Field field1 = new Field("field1", enumSchema); + schema.setFields(Collections.singletonList(field1)); + + GenericDatumWriter datumWriter = new GenericDatumWriter<>(schema); + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + Encoder encoder = EncoderFactory.get().binaryEncoder(byteArrayOutputStream, null); + GenericRecordBuilder builder = new GenericRecordBuilder(schema); + String badValue = "GOODBYE"; + builder.set(field1, new EnumSymbol(enumSchema, badValue)); + Record record = builder.build(); + try { + datumWriter.write(record, encoder); + fail("should have thrown"); + } catch (AvroTypeException ate) { + assertTrue(ate.getMessage().contains(goodValue)); + assertTrue(ate.getMessage().contains(badValue)); + } + } }