From 9fba3e2fe3326b4ee6d18dded3744c30577c1b35 Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Mon, 15 Jan 2024 11:09:07 -0800 Subject: [PATCH 1/2] Add failing test for #1144 --- .../FilteringOnAsyncInfiniteLoop1144Test.java | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 src/test/java/com/fasterxml/jackson/failing/async/FilteringOnAsyncInfiniteLoop1144Test.java diff --git a/src/test/java/com/fasterxml/jackson/failing/async/FilteringOnAsyncInfiniteLoop1144Test.java b/src/test/java/com/fasterxml/jackson/failing/async/FilteringOnAsyncInfiniteLoop1144Test.java new file mode 100644 index 0000000000..3321403229 --- /dev/null +++ b/src/test/java/com/fasterxml/jackson/failing/async/FilteringOnAsyncInfiniteLoop1144Test.java @@ -0,0 +1,50 @@ +package com.fasterxml.jackson.failing.async; + +import java.nio.charset.StandardCharsets; +import java.time.Duration; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +import org.assertj.core.api.Assertions; +import org.junit.Test; + +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.core.async.ByteArrayFeeder; +import com.fasterxml.jackson.core.filter.FilteringParserDelegate; +import com.fasterxml.jackson.core.filter.JsonPointerBasedFilter; +import com.fasterxml.jackson.core.filter.TokenFilter; + +public class FilteringOnAsyncInfiniteLoop1144Test +{ + @SuppressWarnings("resource") + @Test + public void testFilteringNonBlockingParser() throws Exception + { + JsonFactory factory = new JsonFactoryBuilder().build(); + JsonParser nonBlockingParser = factory.createNonBlockingByteArrayParser(); + ByteArrayFeeder inputFeeder = (ByteArrayFeeder) nonBlockingParser.getNonBlockingInputFeeder(); + String json = "{\"first\":1,\"second\":2}"; + byte[] jsonBytes = json.getBytes(StandardCharsets.UTF_8); + + JsonParser filteringParser = new FilteringParserDelegate(nonBlockingParser, + new JsonPointerBasedFilter("/second"), + TokenFilter.Inclusion.ONLY_INCLUDE_ALL, false); + + ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(); + executor.schedule(() -> { + try { + inputFeeder.feedInput(jsonBytes, 0, jsonBytes.length); + } catch (Exception e) { + throw new RuntimeException(e); + } + }, 500, TimeUnit.MILLISECONDS); + + Future future = Executors.newSingleThreadExecutor().submit(() -> filteringParser.nextToken()); + Assertions.assertThat(future) + .succeedsWithin(Duration.ofSeconds(5)) + .isNotNull() + .isNotEqualTo(JsonToken.NOT_AVAILABLE); + } +} From 49f716a4122d99cb97e2c108b03a7fb8b2b565cc Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Mon, 15 Jan 2024 11:21:54 -0800 Subject: [PATCH 2/2] Minor changes to test --- .../FilteringOnAsyncInfiniteLoop1144Test.java | 38 ++++++++++++++++--- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/src/test/java/com/fasterxml/jackson/failing/async/FilteringOnAsyncInfiniteLoop1144Test.java b/src/test/java/com/fasterxml/jackson/failing/async/FilteringOnAsyncInfiniteLoop1144Test.java index 3321403229..7829801b61 100644 --- a/src/test/java/com/fasterxml/jackson/failing/async/FilteringOnAsyncInfiniteLoop1144Test.java +++ b/src/test/java/com/fasterxml/jackson/failing/async/FilteringOnAsyncInfiniteLoop1144Test.java @@ -16,17 +16,44 @@ import com.fasterxml.jackson.core.filter.JsonPointerBasedFilter; import com.fasterxml.jackson.core.filter.TokenFilter; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + public class FilteringOnAsyncInfiniteLoop1144Test { + private final JsonFactory JSON_F = new JsonFactory(); + + private final byte[] DOC = "{\"first\":1,\"second\":2}" + .getBytes(StandardCharsets.UTF_8); + + // Just to show expected filtering behavior with blocking alternative + @Test + public void testFilteringBlockingParser() throws Exception + { + try (JsonParser p = JSON_F.createParser(DOC)) { + try (JsonParser fp = new FilteringParserDelegate(p, + new JsonPointerBasedFilter("/second"), + TokenFilter.Inclusion.ONLY_INCLUDE_ALL, false)) { + assertEquals(JsonToken.VALUE_NUMBER_INT, fp.nextToken()); + assertEquals(2, fp.getIntValue()); + assertNull(fp.nextToken()); + } + } + } + + // And here's reproduction of infinite loop @SuppressWarnings("resource") @Test public void testFilteringNonBlockingParser() throws Exception { - JsonFactory factory = new JsonFactoryBuilder().build(); - JsonParser nonBlockingParser = factory.createNonBlockingByteArrayParser(); + JsonParser nonBlockingParser = JSON_F.createNonBlockingByteArrayParser(); ByteArrayFeeder inputFeeder = (ByteArrayFeeder) nonBlockingParser.getNonBlockingInputFeeder(); - String json = "{\"first\":1,\"second\":2}"; - byte[] jsonBytes = json.getBytes(StandardCharsets.UTF_8); + + // If we did this, things would work: + /* + inputFeeder.feedInput(DOC, 0, DOC.length); + inputFeeder.endOfInput(); + */ JsonParser filteringParser = new FilteringParserDelegate(nonBlockingParser, new JsonPointerBasedFilter("/second"), @@ -35,7 +62,8 @@ public void testFilteringNonBlockingParser() throws Exception ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(); executor.schedule(() -> { try { - inputFeeder.feedInput(jsonBytes, 0, jsonBytes.length); + // This doesn't seem to make a difference: + inputFeeder.feedInput(DOC, 0, DOC.length); } catch (Exception e) { throw new RuntimeException(e); }