mirror of https://github.com/apache/kafka.git
KAFKA-6084: Propagate JSON parsing errors in ReassignPartitionsCommand (#4090)
Reviewers: Ismael Juma <ismael@juma.me.uk>
This commit is contained in:
parent
3717ab0ff2
commit
1aac64667f
|
|
@ -40,6 +40,7 @@ import scala.collection.{Map, Seq, mutable}
|
|||
import scala.compat.java8.OptionConverters._
|
||||
import scala.math.Ordered.orderingToOrdered
|
||||
|
||||
|
||||
object ReassignPartitionsCommand extends Logging {
|
||||
private[admin] val AnyLogDir = "any"
|
||||
|
||||
|
|
@ -1622,14 +1623,15 @@ object ReassignPartitionsCommand extends Logging {
|
|||
}
|
||||
|
||||
def parsePartitionReassignmentData(jsonData: String): (Seq[(TopicPartition, Seq[Int])], Map[TopicPartitionReplica, String]) = {
|
||||
Json.parseFull(jsonData) match {
|
||||
case Some(js) =>
|
||||
Json.tryParseFull(jsonData) match {
|
||||
case Right(js) =>
|
||||
val version = js.asJsonObject.get("version") match {
|
||||
case Some(jsonValue) => jsonValue.to[Int]
|
||||
case None => EarliestVersion
|
||||
}
|
||||
parsePartitionReassignmentData(version, js)
|
||||
case None => throw new AdminOperationException("The input string is not a valid JSON")
|
||||
case Left(f) =>
|
||||
throw new AdminOperationException(f)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,8 +16,9 @@
|
|||
*/
|
||||
package kafka.utils
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException
|
||||
import com.fasterxml.jackson.core.{JsonParseException, JsonProcessingException}
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
import com.fasterxml.jackson.databind.node.MissingNode
|
||||
import kafka.utils.json.JsonValue
|
||||
|
||||
import scala.reflect.ClassTag
|
||||
|
|
@ -32,9 +33,7 @@ object Json {
|
|||
/**
|
||||
* Parse a JSON string into a JsonValue if possible. `None` is returned if `input` is not valid JSON.
|
||||
*/
|
||||
def parseFull(input: String): Option[JsonValue] =
|
||||
try Option(mapper.readTree(input)).map(JsonValue(_))
|
||||
catch { case _: JsonProcessingException => None }
|
||||
def parseFull(input: String): Option[JsonValue] = tryParseFull(input).toOption
|
||||
|
||||
/**
|
||||
* Parse a JSON string into either a generic type T, or a JsonProcessingException in the case of
|
||||
|
|
@ -64,6 +63,19 @@ object Json {
|
|||
catch { case e: JsonProcessingException => Left(e) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a JSON string into a JsonValue if possible. It returns an `Either` where `Left` will be an exception and
|
||||
* `Right` is the `JsonValue`.
|
||||
* @param input a JSON string to parse
|
||||
* @return An `Either` which in case of `Left` means an exception and `Right` is the actual return value.
|
||||
*/
|
||||
def tryParseFull(input: String): Either[JsonProcessingException, JsonValue] =
|
||||
if (input == null || input.isEmpty)
|
||||
Left(new JsonParseException(MissingNode.getInstance().traverse(), "The input string shouldn't be empty"))
|
||||
else
|
||||
try Right(mapper.readTree(input)).map(JsonValue(_))
|
||||
catch { case e: JsonProcessingException => Left(e) }
|
||||
|
||||
/**
|
||||
* Encode an object into a JSON string. This method accepts any type supported by Jackson's ObjectMapper in
|
||||
* the default configuration. That is, Java collections are supported, but Scala collections are not (to avoid
|
||||
|
|
|
|||
|
|
@ -659,4 +659,18 @@ class ReassignPartitionsUnitTest {
|
|||
assertTrue("Expected the string to start with %s, but it was %s".format(prefix, str),
|
||||
str.startsWith(prefix))
|
||||
}
|
||||
|
||||
@Test
|
||||
def testPropagateInvalidJsonError(): Unit = {
|
||||
val adminClient = new MockAdminClient.Builder().numBrokers(4).build()
|
||||
try {
|
||||
addTopics(adminClient)
|
||||
assertStartsWith("Unexpected character",
|
||||
assertThrows(
|
||||
classOf[AdminOperationException], () => executeAssignment(adminClient, additional = false,
|
||||
"{invalid_json")).getMessage)
|
||||
} finally {
|
||||
adminClient.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ package kafka.utils
|
|||
import java.nio.charset.StandardCharsets
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty
|
||||
import com.fasterxml.jackson.core.JsonParseException
|
||||
import com.fasterxml.jackson.core.{JsonParseException, JsonProcessingException}
|
||||
import com.fasterxml.jackson.databind.JsonNode
|
||||
import com.fasterxml.jackson.databind.node._
|
||||
import kafka.utils.JsonTest.TestObject
|
||||
|
|
@ -40,25 +40,34 @@ class JsonTest {
|
|||
def testJsonParse(): Unit = {
|
||||
val jnf = JsonNodeFactory.instance
|
||||
|
||||
assertEquals(Json.parseFull("{}"), Some(JsonValue(new ObjectNode(jnf))))
|
||||
assertEquals(Some(JsonValue(new ObjectNode(jnf))), Json.parseFull("{}"))
|
||||
assertEquals(Right(JsonValue(new ObjectNode(jnf))), Json.tryParseFull("{}"))
|
||||
assertEquals(classOf[Left[JsonProcessingException, JsonValue]], Json.tryParseFull(null).getClass)
|
||||
assertThrows(classOf[IllegalArgumentException], () => Json.tryParseBytes(null))
|
||||
|
||||
assertEquals(Json.parseFull("""{"foo":"bar"s}"""), None)
|
||||
assertEquals(None, Json.parseFull(""))
|
||||
assertEquals(classOf[Left[JsonProcessingException, JsonValue]], Json.tryParseFull("").getClass)
|
||||
|
||||
assertEquals(None, Json.parseFull("""{"foo":"bar"s}"""))
|
||||
val tryRes = Json.tryParseFull("""{"foo":"bar"s}""")
|
||||
assertTrue(tryRes.isInstanceOf[Left[_, JsonValue]])
|
||||
|
||||
val objectNode = new ObjectNode(
|
||||
jnf,
|
||||
Map[String, JsonNode]("foo" -> new TextNode("bar"), "is_enabled" -> BooleanNode.TRUE).asJava
|
||||
)
|
||||
assertEquals(Json.parseFull("""{"foo":"bar", "is_enabled":true}"""), Some(JsonValue(objectNode)))
|
||||
assertEquals(Some(JsonValue(objectNode)), Json.parseFull("""{"foo":"bar", "is_enabled":true}"""))
|
||||
assertEquals(Right(JsonValue(objectNode)), Json.tryParseFull("""{"foo":"bar", "is_enabled":true}"""))
|
||||
|
||||
val arrayNode = new ArrayNode(jnf)
|
||||
Vector(1, 2, 3).map(new IntNode(_)).foreach(arrayNode.add)
|
||||
assertEquals(Json.parseFull("[1, 2, 3]"), Some(JsonValue(arrayNode)))
|
||||
assertEquals(Some(JsonValue(arrayNode)), Json.parseFull("[1, 2, 3]"))
|
||||
|
||||
// Test with encoder that properly escapes backslash and quotes
|
||||
val map = Map("foo1" -> """bar1\,bar2""", "foo2" -> """\bar""").asJava
|
||||
val encoded = Json.encodeAsString(map)
|
||||
val decoded = Json.parseFull(encoded)
|
||||
assertEquals(Json.parseFull("""{"foo1":"bar1\\,bar2", "foo2":"\\bar"}"""), decoded)
|
||||
assertEquals(decoded, Json.parseFull("""{"foo1":"bar1\\,bar2", "foo2":"\\bar"}"""))
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
|||
Loading…
Reference in New Issue