This is an automated email from the ASF dual-hosted git repository. gwenshap pushed a commit to branch trunk in repository https://gitbox.apache.org/repos/asf/kafka.git
The following commit(s) were added to refs/heads/trunk by this push: new d70fe5e KAFKA-8644; The Kafka protocol generator should allow null defaults for bytes and array fields d70fe5e is described below commit d70fe5ed2d25af432ec6ce02ccfeaa40bcd2f295 Author: Colin P. Mccabe <cmcc...@confluent.io> AuthorDate: Thu Jul 11 09:52:01 2019 -0700 KAFKA-8644; The Kafka protocol generator should allow null defaults for bytes and array fields Author: Colin P. Mccabe <cmcc...@confluent.io> Reviewers: Stanislav Kozlovski, Gwen Shapira Closes #7059 from cmccabe/KAFKA-8644 --- .../apache/kafka/message/MessageDataGenerator.java | 42 +++++++--- .../kafka/message/MessageDataGeneratorTest.java | 93 ++++++++++++++++++++++ 2 files changed, 124 insertions(+), 11 deletions(-) diff --git a/generator/src/main/java/org/apache/kafka/message/MessageDataGenerator.java b/generator/src/main/java/org/apache/kafka/message/MessageDataGenerator.java index ee0c3df..d0e5044 100644 --- a/generator/src/main/java/org/apache/kafka/message/MessageDataGenerator.java +++ b/generator/src/main/java/org/apache/kafka/message/MessageDataGenerator.java @@ -937,9 +937,17 @@ public final class MessageDataGenerator { buffer.incrementIndent(); headerGenerator.addImport(MessageGenerator.UNSUPPORTED_VERSION_EXCEPTION_CLASS); if (field.type().isArray()) { - buffer.printf("if (!%s.isEmpty()) {%n", field.camelCaseName()); + if (fieldDefault(field).equals("null")) { + buffer.printf("if (%s != null) {%n", field.camelCaseName()); + } else { + buffer.printf("if (!%s.isEmpty()) {%n", field.camelCaseName()); + } } else if (field.type().isBytes()) { - buffer.printf("if (%s.length != 0) {%n", field.camelCaseName()); + if (fieldDefault(field).equals("null")) { + buffer.printf("if (%s != null) {%n", field.camelCaseName()); + } else { + buffer.printf("if (%s.length != 0) {%n", field.camelCaseName()); + } } else if (field.type().isString()) { if (fieldDefault(field).equals("null")) { buffer.printf("if (%s != null) {%n", field.camelCaseName()); @@ -1221,19 +1229,19 @@ public final class MessageDataGenerator { } } else if (field.type() instanceof FieldType.StringFieldType) { if (field.defaultString().equals("null")) { - if (!(field.nullableVersions().contains(field.versions()))) { - throw new RuntimeException("null cannot be the default for field " + - field.name() + ", because not all versions of this field are " + - "nullable."); - } + validateNullDefault(field); return "null"; } else { return "\"" + field.defaultString() + "\""; } } else if (field.type().isBytes()) { - if (!field.defaultString().isEmpty()) { + if (field.defaultString().equals("null")) { + validateNullDefault(field); + return "null"; + } else if (!field.defaultString().isEmpty()) { throw new RuntimeException("Invalid default for bytes field " + - field.name() + ": custom defaults are not supported for bytes fields."); + field.name() + ". The only valid default for a bytes field " + + "is empty or null."); } headerGenerator.addImport(MessageGenerator.BYTES_CLASS); return "Bytes.EMPTY"; @@ -1244,9 +1252,13 @@ public final class MessageDataGenerator { } return "new " + field.type().toString() + "()"; } else if (field.type().isArray()) { - if (!field.defaultString().isEmpty()) { + if (field.defaultString().equals("null")) { + validateNullDefault(field); + return "null"; + } else if (!field.defaultString().isEmpty()) { throw new RuntimeException("Invalid default for array field " + - field.name() + ": custom defaults are not supported for array fields."); + field.name() + ". The only valid default for an array field " + + "is the empty array or null."); } FieldType.ArrayType arrayType = (FieldType.ArrayType) field.type(); if (structRegistry.isStructArrayWithKeys(field)) { @@ -1260,6 +1272,14 @@ public final class MessageDataGenerator { } } + private void validateNullDefault(FieldSpec field) { + if (!(field.nullableVersions().contains(field.versions()))) { + throw new RuntimeException("null cannot be the default for field " + + field.name() + ", because not all versions of this field are " + + "nullable."); + } + } + private void generateFieldAccessor(FieldSpec field) { buffer.printf("%n"); generateAccessor(fieldAbstractJavaType(field), field.camelCaseName(), diff --git a/generator/src/test/java/org/apache/kafka/message/MessageDataGeneratorTest.java b/generator/src/test/java/org/apache/kafka/message/MessageDataGeneratorTest.java new file mode 100644 index 0000000..e8fcaf2 --- /dev/null +++ b/generator/src/test/java/org/apache/kafka/message/MessageDataGeneratorTest.java @@ -0,0 +1,93 @@ +/* + * 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.kafka.message; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.Timeout; + +import java.util.Arrays; + +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +public class MessageDataGeneratorTest { + @Rule + final public Timeout globalTimeout = Timeout.millis(120000); + + @Test + public void testNullDefaults() throws Exception { + MessageSpec testMessageSpec = MessageGenerator.JSON_SERDE.readValue(String.join("", Arrays.asList( + "{", + " \"type\": \"request\",", + " \"name\": \"FooBar\",", + " \"validVersions\": \"0-2\",", + " \"fields\": [", + " { \"name\": \"field1\", \"type\": \"int32\", \"versions\": \"0+\" },", + " { \"name\": \"field2\", \"type\": \"[]TestStruct\", \"versions\": \"1+\", ", + " \"nullableVersions\": \"1+\", \"default\": \"null\", \"fields\": [", + " { \"name\": \"field1\", \"type\": \"int32\", \"versions\": \"0+\" }", + " ]},", + " { \"name\": \"field3\", \"type\": \"bytes\", \"versions\": \"2+\", ", + " \"nullableVersions\": \"2+\", \"default\": \"null\" }", + " ]", + "}")), MessageSpec.class); + new MessageDataGenerator().generate(testMessageSpec); + } + + @Test + public void testInvalidNullDefaultForInt() throws Exception { + MessageSpec testMessageSpec = MessageGenerator.JSON_SERDE.readValue(String.join("", Arrays.asList( + "{", + " \"type\": \"request\",", + " \"name\": \"FooBar\",", + " \"validVersions\": \"0-2\",", + " \"fields\": [", + " { \"name\": \"field1\", \"type\": \"int32\", \"versions\": \"0+\", \"default\": \"null\" }", + " ]", + "}")), MessageSpec.class); + try { + new MessageDataGenerator().generate(testMessageSpec); + fail("Expected MessageDataGenerator#generate to fail"); + } catch (Throwable e) { + assertTrue("Invalid error message: " + e.getMessage(), + e.getMessage().contains("Invalid default for int32")); + } + } + + @Test + public void testInvalidNullDefaultForPotentiallyNonNullableArray() throws Exception { + MessageSpec testMessageSpec = MessageGenerator.JSON_SERDE.readValue(String.join("", Arrays.asList( + "{", + " \"type\": \"request\",", + " \"name\": \"FooBar\",", + " \"validVersions\": \"0-2\",", + " \"fields\": [", + " { \"name\": \"field1\", \"type\": \"[]int32\", \"versions\": \"0+\", \"nullableVersions\": \"1+\", ", + " \"default\": \"null\" }", + " ]", + "}")), MessageSpec.class); + try { + new MessageDataGenerator().generate(testMessageSpec); + fail("Expected MessageDataGenerator#generate to fail"); + } catch (RuntimeException e) { + assertTrue("Invalid error message: " + e.getMessage(), + e.getMessage().contains("not all versions of this field are nullable")); + } + } +}