From 075e98bafd806f85e7a3b25bf82f7319157bb7ad Mon Sep 17 00:00:00 2001 From: joohyukkim Date: Wed, 2 Aug 2023 09:12:52 +0900 Subject: [PATCH 1/6] Add new JsonFactory.builder --- .../fasterxml/jackson/core/JsonFactory.java | 60 ++++++-- .../jackson/core/JsonProcessingException.java | 2 + .../fasterxml/jackson/core/TSFBuilder.java | 20 +++ ...tConfigurationMaxErrorTokenLengthTest.java | 132 ++++++++++++++++++ 4 files changed, 200 insertions(+), 14 deletions(-) create mode 100644 src/test/java/com/fasterxml/jackson/core/ErrorReportConfigurationMaxErrorTokenLengthTest.java diff --git a/src/main/java/com/fasterxml/jackson/core/JsonFactory.java b/src/main/java/com/fasterxml/jackson/core/JsonFactory.java index 3fa4240070..6564891e6a 100644 --- a/src/main/java/com/fasterxml/jackson/core/JsonFactory.java +++ b/src/main/java/com/fasterxml/jackson/core/JsonFactory.java @@ -49,8 +49,8 @@ */ @SuppressWarnings("resource") public class JsonFactory - extends TokenStreamFactory - implements Versioned, + extends TokenStreamFactory + implements Versioned, java.io.Serializable // since 2.1 (for Android, mostly) { private static final long serialVersionUID = 2; @@ -66,7 +66,7 @@ public class JsonFactory * changed for {@link JsonFactory}. */ public enum Feature - implements JacksonFeature // since 2.12 + implements JacksonFeature // since 2.12 { // // // Symbol handling (interning etc) @@ -284,6 +284,14 @@ public static int collectDefaults() { */ protected StreamReadConstraints _streamReadConstraints; + /** + * Container for configuration values used when handling errorneous token inputs. + * + * @see ErrorReportConfiguration + * @since 2.16 + */ + protected ErrorReportConfiguration _errorReportConfiguration; + /** * Write constraints to use for {@link JsonGenerator}s constructed using * this factory. @@ -311,7 +319,7 @@ public static int collectDefaults() { * @since 2.16 */ protected final List _generatorDecorators; - + /** * Separator used between root-level values, if any; null indicates * "do not add separator". @@ -361,6 +369,7 @@ public JsonFactory(ObjectCodec oc) { _quoteChar = DEFAULT_QUOTE_CHAR; _streamReadConstraints = StreamReadConstraints.defaults(); _streamWriteConstraints = StreamWriteConstraints.defaults(); + _errorReportConfiguration = ErrorReportConfiguration.defaults(); _generatorDecorators = null; } @@ -385,6 +394,7 @@ protected JsonFactory(JsonFactory src, ObjectCodec codec) _generatorDecorators = _copy(src._generatorDecorators); _streamReadConstraints = Objects.requireNonNull(src._streamReadConstraints); _streamWriteConstraints = Objects.requireNonNull(src._streamWriteConstraints); + _errorReportConfiguration = Objects.requireNonNull(src._errorReportConfiguration); // JSON-specific _characterEscapes = src._characterEscapes; @@ -412,6 +422,7 @@ public JsonFactory(JsonFactoryBuilder b) { _generatorDecorators = _copy(b._generatorDecorators); _streamReadConstraints = Objects.requireNonNull(b._streamReadConstraints); _streamWriteConstraints = Objects.requireNonNull(b._streamWriteConstraints); + _errorReportConfiguration = Objects.requireNonNull(b._errorReportConfiguration); // JSON-specific _characterEscapes = b._characterEscapes; @@ -439,6 +450,7 @@ protected JsonFactory(TSFBuilder b, boolean bogus) { _generatorDecorators = _copy(b._generatorDecorators); _streamReadConstraints = Objects.requireNonNull(b._streamReadConstraints); _streamWriteConstraints = Objects.requireNonNull(b._streamWriteConstraints); + _errorReportConfiguration = Objects.requireNonNull(b._errorReportConfiguration); // JSON-specific: need to assign even if not really used _characterEscapes = null; @@ -838,12 +850,32 @@ public JsonFactory setStreamReadConstraints(StreamReadConstraints src) { return this; } + /** + * Method for overriding {@link ErrorReportConfiguration} defined for + * this factory. + *

+ * NOTE: the preferred way to set constraints is by using + * {@link JsonFactoryBuilder#errorReportConfiguration}: this method is only + * provided to support older non-builder-based construction. + * In Jackson 3.x this method will not be available. + * + * @param src Configuration + * + * @return This factory instance (to allow call chaining) + * + * @since 2.16 + */ + public JsonFactory setErrorReportConfiguration(ErrorReportConfiguration src) { + _errorReportConfiguration = Objects.requireNonNull(src, "Cannot pass null ErrorReportConfiguration");; + return this; + } + /** * Method for overriding {@link StreamWriteConstraints} defined for * this factory. *

* NOTE: the preferred way to set constraints is by using - * {@link JsonFactoryBuilder#streamWriteConstraints}: this method is only + * {@link JsonFactoryBuilder#_streamWriteConstraints}: this method is only * provided to support older non-builder-based construction. * In Jackson 3.x this method will not be available. * @@ -1856,10 +1888,10 @@ protected JsonParser _createParser(Reader r, IOContext ctxt) throws IOException * @since 2.4 */ protected JsonParser _createParser(char[] data, int offset, int len, IOContext ctxt, - boolean recyclable) throws IOException { + boolean recyclable) throws IOException { return new ReaderBasedJsonParser(ctxt, _parserFeatures, null, _objectCodec, _rootCharSymbols.makeChild(_factoryFeatures), - data, offset, offset+len, recyclable); + data, offset, offset+len, recyclable); } /** @@ -2112,7 +2144,7 @@ protected IOContext _createContext(ContentReference contentRef, boolean resource if (contentRef == null) { contentRef = ContentReference.unknown(); } - return new IOContext(_streamReadConstraints, _streamWriteConstraints, + return new IOContext(_streamReadConstraints, _streamWriteConstraints, _errorReportConfiguration, _getBufferRecycler(), contentRef, resourceManaged); } @@ -2128,7 +2160,7 @@ protected IOContext _createContext(ContentReference contentRef, boolean resource */ @Deprecated // @since 2.13 protected IOContext _createContext(Object rawContentRef, boolean resourceManaged) { - return new IOContext(_streamReadConstraints, _streamWriteConstraints, + return new IOContext(_streamReadConstraints, _streamWriteConstraints, _errorReportConfiguration, _getBufferRecycler(), _createContentReference(rawContentRef), resourceManaged); @@ -2147,7 +2179,7 @@ protected IOContext _createContext(Object rawContentRef, boolean resourceManaged protected IOContext _createNonBlockingContext(Object srcRef) { // [jackson-core#479]: allow recycling for non-blocking parser again // now that access is thread-safe - return new IOContext(_streamReadConstraints, _streamWriteConstraints, + return new IOContext(_streamReadConstraints, _streamWriteConstraints, _errorReportConfiguration, _getBufferRecycler(), _createContentReference(srcRef), false); @@ -2168,7 +2200,7 @@ protected IOContext _createNonBlockingContext(Object srcRef) { protected ContentReference _createContentReference(Object contentAccessor) { // 21-Mar-2021, tatu: For now assume "canHandleBinaryNatively()" is reliable // indicator of textual vs binary format: - return ContentReference.construct(!canHandleBinaryNatively(), contentAccessor); + return ContentReference.construct(!canHandleBinaryNatively(), contentAccessor, _errorReportConfiguration); } /** @@ -2187,12 +2219,12 @@ protected ContentReference _createContentReference(Object contentAccessor) { * @since 2.13 */ protected ContentReference _createContentReference(Object contentAccessor, - int offset, int length) + int offset, int length) { // 21-Mar-2021, tatu: For now assume "canHandleBinaryNatively()" is reliable // indicator of textual vs binary format: return ContentReference.construct(!canHandleBinaryNatively(), - contentAccessor, offset, length); + contentAccessor, offset, length, _errorReportConfiguration); } /* @@ -2226,4 +2258,4 @@ private final boolean _isJSONFactory() { // or its sub-class / delegated to one, no need to check for equality, identity is enough return getFormatName() == FORMAT_NAME_JSON; } -} +} \ No newline at end of file diff --git a/src/main/java/com/fasterxml/jackson/core/JsonProcessingException.java b/src/main/java/com/fasterxml/jackson/core/JsonProcessingException.java index e3364e9039..11d1cd88db 100644 --- a/src/main/java/com/fasterxml/jackson/core/JsonProcessingException.java +++ b/src/main/java/com/fasterxml/jackson/core/JsonProcessingException.java @@ -14,6 +14,8 @@ *

* Since Jackson 2.12 extends intermediate {@link JacksonException} type * instead of directly extending {@link java.io.IOException}. + *

+ * Since Jackson 2.16, handles its content as configured using {@link com.fasterxml.jackson.core.ErrorReportConfiguration}. */ public class JsonProcessingException extends JacksonException { diff --git a/src/main/java/com/fasterxml/jackson/core/TSFBuilder.java b/src/main/java/com/fasterxml/jackson/core/TSFBuilder.java index 02fe83b0eb..e3984daf8c 100644 --- a/src/main/java/com/fasterxml/jackson/core/TSFBuilder.java +++ b/src/main/java/com/fasterxml/jackson/core/TSFBuilder.java @@ -100,6 +100,13 @@ public abstract class TSFBuilder _generatorDecorators; + /** + * Optional {@link ErrorReportConfiguration} to use. + * + * @since 2.16 + */ + protected ErrorReportConfiguration _errorReportConfiguration; + /* /********************************************************************** /* Construction @@ -120,6 +127,7 @@ protected TSFBuilder(JsonFactory base) _outputDecorator = base._outputDecorator; _streamReadConstraints = base._streamReadConstraints; _streamWriteConstraints = base._streamWriteConstraints; + _errorReportConfiguration = base._errorReportConfiguration; _generatorDecorators = _copy(base._generatorDecorators); } @@ -134,6 +142,7 @@ protected TSFBuilder(int factoryFeatures, _outputDecorator = null; _streamReadConstraints = StreamReadConstraints.defaults(); _streamWriteConstraints = StreamWriteConstraints.defaults(); + _errorReportConfiguration = ErrorReportConfiguration.defaults(); _generatorDecorators = null; } @@ -324,6 +333,17 @@ public B streamReadConstraints(StreamReadConstraints streamReadConstraints) { return _this(); } + /** + * Sets the configuration for error tokens. + * + * @param errorReportConfiguration configuration values used for handling errorneous token inputs. + * @return this factory + * @since 2.16 + */ + public B errorReportConfiguration(ErrorReportConfiguration errorReportConfiguration) { + _errorReportConfiguration = errorReportConfiguration; + return _this(); + } /** * Sets the constraints for streaming writes. * diff --git a/src/test/java/com/fasterxml/jackson/core/ErrorReportConfigurationMaxErrorTokenLengthTest.java b/src/test/java/com/fasterxml/jackson/core/ErrorReportConfigurationMaxErrorTokenLengthTest.java new file mode 100644 index 0000000000..2c9980627b --- /dev/null +++ b/src/test/java/com/fasterxml/jackson/core/ErrorReportConfigurationMaxErrorTokenLengthTest.java @@ -0,0 +1,132 @@ +package com.fasterxml.jackson.core; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Unit tests for class {@link ErrorReportConfiguration#getMaxErrorTokenLength()}. + */ +public class ErrorReportConfigurationMaxErrorTokenLengthTest extends BaseTest +{ + + // Should be 256 + public final static int DEFAULT_LENGTH = ErrorReportConfiguration.DEFAULT_MAX_ERROR_TOKEN_LENGTH; + + /* + /********************************************************** + /* Unit Tests + /********************************************************** + */ + + public void testExpectedTokenLengthWithConfigurations() + throws Exception + { + // default + _verifyErrorTokenLength(263, + ErrorReportConfiguration.builder().build()); + + // default + _verifyErrorTokenLength(263, + ErrorReportConfiguration.defaults()); + + // shorter + _verifyErrorTokenLength(63, + ErrorReportConfiguration.builder() + .maxErrorTokenLength(DEFAULT_LENGTH - 200).build()); + + // longer + _verifyErrorTokenLength(463, + ErrorReportConfiguration.builder() + .maxErrorTokenLength(DEFAULT_LENGTH + 200).build()); + + // zero + _verifyErrorTokenLength(9, + ErrorReportConfiguration.builder() + .maxErrorTokenLength(0).build()); + + // negative value fails + try { + _verifyErrorTokenLength(9, + ErrorReportConfiguration.builder() + .maxErrorTokenLength(-1).build()); + } catch (IllegalArgumentException e) { + _verifyIllegalArgumentExceptionMessage(e.getMessage()); + } + + // null is not allowed + try { + _verifyErrorTokenLength(263, + null); + } catch (NullPointerException e) { + // no-op + } + } + + public void testNegativeBuilderConfiguration() + { + // Zero should be ok + ErrorReportConfiguration.builder().maxErrorTokenLength(0).build(); + + // But not -1 + try { + ErrorReportConfiguration.builder().maxErrorTokenLength(-1).build(); + fail(); + } catch (IllegalArgumentException e) { + _verifyIllegalArgumentExceptionMessage(e.getMessage()); + } + } + + public void testNullSetterThrowsException() { + try { + newStreamFactory().setErrorReportConfiguration(null); + fail(); + } catch (NullPointerException npe) { + assertThat(npe).hasMessage("Cannot pass null ErrorReportConfiguration"); + } + } + + /* + /********************************************************** + /* Internal helper methods + /********************************************************** + */ + + private void _verifyIllegalArgumentExceptionMessage(String message) { + assertThat(message) + .contains("Value of maxErrorTokenLength") + .contains("cannot be negative"); + } + + private void _verifyErrorTokenLength(int expectedTokenLen, ErrorReportConfiguration errorReportConfiguration) + throws Exception + { + JsonFactory jf3 = streamFactoryBuilder() + .errorReportConfiguration(errorReportConfiguration) + .build(); + _testWithMaxErrorTokenLength(expectedTokenLen, + // creating arbitrary number so that token reaches max len, but not over-do it + 50 * DEFAULT_LENGTH, jf3); + } + + private void _testWithMaxErrorTokenLength(int expectedSize, int tokenLen, JsonFactory factory) + throws Exception + { + String inputWithDynamicLength = _buildBrokenJsonOfLength(tokenLen); + try (JsonParser parser = factory.createParser(inputWithDynamicLength)) { + parser.nextToken(); + parser.nextToken(); + } catch (JsonProcessingException e) { + assertThat(e.getLocation()._totalChars).isEqualTo(expectedSize); + assertThat(e.getMessage()).contains("Unrecognized token"); + } + } + + private String _buildBrokenJsonOfLength(int len) + { + StringBuilder sb = new StringBuilder("{\"key\":"); + for (int i = 0; i < len; i++) { + sb.append("a"); + } + sb.append("!}"); + return sb.toString(); + } +} \ No newline at end of file From d050976349d3693217c2f8740810565953e0e727 Mon Sep 17 00:00:00 2001 From: joohyukkim Date: Wed, 2 Aug 2023 10:02:19 +0900 Subject: [PATCH 2/6] Update JsonFactory.java --- .../com/fasterxml/jackson/core/JsonFactory.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/fasterxml/jackson/core/JsonFactory.java b/src/main/java/com/fasterxml/jackson/core/JsonFactory.java index 6564891e6a..ace5500ea1 100644 --- a/src/main/java/com/fasterxml/jackson/core/JsonFactory.java +++ b/src/main/java/com/fasterxml/jackson/core/JsonFactory.java @@ -49,8 +49,8 @@ */ @SuppressWarnings("resource") public class JsonFactory - extends TokenStreamFactory - implements Versioned, + extends TokenStreamFactory + implements Versioned, java.io.Serializable // since 2.1 (for Android, mostly) { private static final long serialVersionUID = 2; @@ -66,7 +66,7 @@ public class JsonFactory * changed for {@link JsonFactory}. */ public enum Feature - implements JacksonFeature // since 2.12 + implements JacksonFeature // since 2.12 { // // // Symbol handling (interning etc) @@ -319,7 +319,7 @@ public static int collectDefaults() { * @since 2.16 */ protected final List _generatorDecorators; - + /** * Separator used between root-level values, if any; null indicates * "do not add separator". @@ -1888,10 +1888,10 @@ protected JsonParser _createParser(Reader r, IOContext ctxt) throws IOException * @since 2.4 */ protected JsonParser _createParser(char[] data, int offset, int len, IOContext ctxt, - boolean recyclable) throws IOException { + boolean recyclable) throws IOException { return new ReaderBasedJsonParser(ctxt, _parserFeatures, null, _objectCodec, _rootCharSymbols.makeChild(_factoryFeatures), - data, offset, offset+len, recyclable); + data, offset, offset+len, recyclable); } /** @@ -2258,4 +2258,4 @@ private final boolean _isJSONFactory() { // or its sub-class / delegated to one, no need to check for equality, identity is enough return getFormatName() == FORMAT_NAME_JSON; } -} \ No newline at end of file +} From 5358acafe3c3e250928b11f08b4e57d4c7be9e1e Mon Sep 17 00:00:00 2001 From: joohyukkim Date: Wed, 2 Aug 2023 10:04:35 +0900 Subject: [PATCH 3/6] Remove unrelated change --- src/main/java/com/fasterxml/jackson/core/JsonFactory.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/fasterxml/jackson/core/JsonFactory.java b/src/main/java/com/fasterxml/jackson/core/JsonFactory.java index ace5500ea1..c02ab9254d 100644 --- a/src/main/java/com/fasterxml/jackson/core/JsonFactory.java +++ b/src/main/java/com/fasterxml/jackson/core/JsonFactory.java @@ -875,7 +875,7 @@ public JsonFactory setErrorReportConfiguration(ErrorReportConfiguration src) { * this factory. *

* NOTE: the preferred way to set constraints is by using - * {@link JsonFactoryBuilder#_streamWriteConstraints}: this method is only + * {@link JsonFactoryBuilder#streamWriteConstraints}: this method is only * provided to support older non-builder-based construction. * In Jackson 3.x this method will not be available. * @@ -2219,7 +2219,7 @@ protected ContentReference _createContentReference(Object contentAccessor) { * @since 2.13 */ protected ContentReference _createContentReference(Object contentAccessor, - int offset, int length) + int offset, int length) { // 21-Mar-2021, tatu: For now assume "canHandleBinaryNatively()" is reliable // indicator of textual vs binary format: From c1dedc604484b8575a8a2b1f899ab83d2d52daf7 Mon Sep 17 00:00:00 2001 From: joohyukkim Date: Wed, 2 Aug 2023 10:52:54 +0900 Subject: [PATCH 4/6] Update ErrorReportConfigurationMaxRawContentLengthTest.java --- ...tConfigurationMaxRawContentLengthTest.java | 97 +++++++++++++++++-- 1 file changed, 88 insertions(+), 9 deletions(-) diff --git a/src/test/java/com/fasterxml/jackson/core/ErrorReportConfigurationMaxRawContentLengthTest.java b/src/test/java/com/fasterxml/jackson/core/ErrorReportConfigurationMaxRawContentLengthTest.java index 3afd9fd011..999404be0d 100644 --- a/src/test/java/com/fasterxml/jackson/core/ErrorReportConfigurationMaxRawContentLengthTest.java +++ b/src/test/java/com/fasterxml/jackson/core/ErrorReportConfigurationMaxRawContentLengthTest.java @@ -1,28 +1,60 @@ package com.fasterxml.jackson.core; +import static org.assertj.core.api.Assertions.assertThat; import com.fasterxml.jackson.core.io.ContentReference; /** * Unit tests for class {@link ErrorReportConfiguration#getMaxRawContentLength()}. */ -public class ErrorReportConfigurationMaxRawContentLengthTest extends BaseTest { +public class ErrorReportConfigurationMaxRawContentLengthTest extends BaseTest +{ + // Should be 256 + public final static int DEFAULT_LENGTH = ErrorReportConfiguration.DEFAULT_MAX_RAW_CONTENT_LENGTH; + /* /********************************************************** /* Unit Tests /********************************************************** */ - public void testBasicToStringErrorConfig() throws Exception { + public void testWithJsonLocation() throws Exception + { // Truncated result - _verifyToString("abc", 2, + _verifyJsonLocationToString("abc", 2, "[Source: (String)\"ab\"[truncated 1 chars]; line: 1, column: 1]"); // Exact length - _verifyToString("abc", 3, + _verifyJsonLocationToString("abc", 3, "[Source: (String)\"abc\"; line: 1, column: 1]"); // Enough length - _verifyToString("abc", 4, + _verifyJsonLocationToString("abc", 4, "[Source: (String)\"abc\"; line: 1, column: 1]"); } + + public void testWithJsonFactory() throws Exception + { + // default + _verifyJsonProcessingExceptionSourceLength(500, + ErrorReportConfiguration.builder().build()); + + // default + _verifyJsonProcessingExceptionSourceLength(500, + ErrorReportConfiguration.defaults()); + + // shorter + _verifyJsonProcessingExceptionSourceLength(499, + ErrorReportConfiguration.builder() + .maxRawContentLength(DEFAULT_LENGTH - 1).build()); + + // longer + _verifyJsonProcessingExceptionSourceLength(501, + ErrorReportConfiguration.builder() + .maxRawContentLength(DEFAULT_LENGTH + 1).build()); + + // zero + _verifyJsonProcessingExceptionSourceLength(0, + ErrorReportConfiguration.builder() + .maxRawContentLength(0).build()); + } /* /********************************************************** @@ -30,19 +62,66 @@ public void testBasicToStringErrorConfig() throws Exception { /********************************************************** */ - private void _verifyToString(String rawSrc, int rawContentLength, String expectedMessage) { + private void _verifyJsonLocationToString(String rawSrc, int rawContentLength, String expectedMessage) + { ContentReference reference = _sourceRefWithErrorReportConfig(rawSrc, rawContentLength); String location = new JsonLocation(reference, 10L, 10L, 1, 1).toString(); assertEquals(expectedMessage, location); } - private ContentReference _sourceRefWithErrorReportConfig(String rawSrc, int rawContentLength) { + private ContentReference _sourceRefWithErrorReportConfig(String rawSrc, int rawContentLength) + { return _sourceRef(rawSrc, ErrorReportConfiguration.builder().maxRawContentLength(rawContentLength).build()); } - private ContentReference _sourceRef(String rawSrc, ErrorReportConfiguration errorReportConfiguration) { - return ContentReference.construct(true, rawSrc, 0, rawSrc.length(),errorReportConfiguration); + private ContentReference _sourceRef(String rawSrc, ErrorReportConfiguration errorReportConfiguration) + { + return ContentReference.construct(true, rawSrc, 0, rawSrc.length(), errorReportConfiguration); + } + + private void _verifyJsonProcessingExceptionSourceLength(int expectedRawContentLength, ErrorReportConfiguration errorReportConfiguration) + throws Exception + { + // Arrange + JsonFactory factory = streamFactoryBuilder() + .enable(StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION) + .errorReportConfiguration(errorReportConfiguration) + .build(); + // Make JSON input too long so it can be cutoff + int tooLongContent = 50 * DEFAULT_LENGTH; + String inputWithDynamicLength = _buildBrokenJsonOfLength(tooLongContent); + + // Act + try (JsonParser parser = factory.createParser(inputWithDynamicLength)) { + parser.nextToken(); + parser.nextToken(); + fail("Should not reach"); + } catch (JsonProcessingException e) { + + // Assert + String prefix = "(String)\""; + String suffix = "\"[truncated 12309 chars]"; + + // The length of the source description should be [ prefix + expected length + suffix ] + int expectedLength = prefix.length() + expectedRawContentLength + suffix.length(); + int actualLength = e.getLocation().sourceDescription().length(); + + assertEquals(expectedLength, actualLength); + assertThat(e.getMessage()) + .contains("Unrecognized token '") + .contains("was expecting (JSON"); + } + } + + private String _buildBrokenJsonOfLength(int len) + { + StringBuilder sb = new StringBuilder("{\"key\":"); + for (int i = 0; i < len; i++) { + sb.append("a"); + } + sb.append("!}"); + return sb.toString(); } } From c66497134a3a3ae857445553d22b42889187ff5e Mon Sep 17 00:00:00 2001 From: joohyukkim Date: Thu, 3 Aug 2023 07:39:22 +0900 Subject: [PATCH 5/6] Merge tests into one --- ...tConfigurationMaxErrorTokenLengthTest.java | 132 ----------- ...tConfigurationMaxRawContentLengthTest.java | 127 ----------- .../core/ErrorReportConfigurationTest.java | 209 +++++++++++++++++- 3 files changed, 203 insertions(+), 265 deletions(-) delete mode 100644 src/test/java/com/fasterxml/jackson/core/ErrorReportConfigurationMaxErrorTokenLengthTest.java delete mode 100644 src/test/java/com/fasterxml/jackson/core/ErrorReportConfigurationMaxRawContentLengthTest.java diff --git a/src/test/java/com/fasterxml/jackson/core/ErrorReportConfigurationMaxErrorTokenLengthTest.java b/src/test/java/com/fasterxml/jackson/core/ErrorReportConfigurationMaxErrorTokenLengthTest.java deleted file mode 100644 index 2c9980627b..0000000000 --- a/src/test/java/com/fasterxml/jackson/core/ErrorReportConfigurationMaxErrorTokenLengthTest.java +++ /dev/null @@ -1,132 +0,0 @@ -package com.fasterxml.jackson.core; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Unit tests for class {@link ErrorReportConfiguration#getMaxErrorTokenLength()}. - */ -public class ErrorReportConfigurationMaxErrorTokenLengthTest extends BaseTest -{ - - // Should be 256 - public final static int DEFAULT_LENGTH = ErrorReportConfiguration.DEFAULT_MAX_ERROR_TOKEN_LENGTH; - - /* - /********************************************************** - /* Unit Tests - /********************************************************** - */ - - public void testExpectedTokenLengthWithConfigurations() - throws Exception - { - // default - _verifyErrorTokenLength(263, - ErrorReportConfiguration.builder().build()); - - // default - _verifyErrorTokenLength(263, - ErrorReportConfiguration.defaults()); - - // shorter - _verifyErrorTokenLength(63, - ErrorReportConfiguration.builder() - .maxErrorTokenLength(DEFAULT_LENGTH - 200).build()); - - // longer - _verifyErrorTokenLength(463, - ErrorReportConfiguration.builder() - .maxErrorTokenLength(DEFAULT_LENGTH + 200).build()); - - // zero - _verifyErrorTokenLength(9, - ErrorReportConfiguration.builder() - .maxErrorTokenLength(0).build()); - - // negative value fails - try { - _verifyErrorTokenLength(9, - ErrorReportConfiguration.builder() - .maxErrorTokenLength(-1).build()); - } catch (IllegalArgumentException e) { - _verifyIllegalArgumentExceptionMessage(e.getMessage()); - } - - // null is not allowed - try { - _verifyErrorTokenLength(263, - null); - } catch (NullPointerException e) { - // no-op - } - } - - public void testNegativeBuilderConfiguration() - { - // Zero should be ok - ErrorReportConfiguration.builder().maxErrorTokenLength(0).build(); - - // But not -1 - try { - ErrorReportConfiguration.builder().maxErrorTokenLength(-1).build(); - fail(); - } catch (IllegalArgumentException e) { - _verifyIllegalArgumentExceptionMessage(e.getMessage()); - } - } - - public void testNullSetterThrowsException() { - try { - newStreamFactory().setErrorReportConfiguration(null); - fail(); - } catch (NullPointerException npe) { - assertThat(npe).hasMessage("Cannot pass null ErrorReportConfiguration"); - } - } - - /* - /********************************************************** - /* Internal helper methods - /********************************************************** - */ - - private void _verifyIllegalArgumentExceptionMessage(String message) { - assertThat(message) - .contains("Value of maxErrorTokenLength") - .contains("cannot be negative"); - } - - private void _verifyErrorTokenLength(int expectedTokenLen, ErrorReportConfiguration errorReportConfiguration) - throws Exception - { - JsonFactory jf3 = streamFactoryBuilder() - .errorReportConfiguration(errorReportConfiguration) - .build(); - _testWithMaxErrorTokenLength(expectedTokenLen, - // creating arbitrary number so that token reaches max len, but not over-do it - 50 * DEFAULT_LENGTH, jf3); - } - - private void _testWithMaxErrorTokenLength(int expectedSize, int tokenLen, JsonFactory factory) - throws Exception - { - String inputWithDynamicLength = _buildBrokenJsonOfLength(tokenLen); - try (JsonParser parser = factory.createParser(inputWithDynamicLength)) { - parser.nextToken(); - parser.nextToken(); - } catch (JsonProcessingException e) { - assertThat(e.getLocation()._totalChars).isEqualTo(expectedSize); - assertThat(e.getMessage()).contains("Unrecognized token"); - } - } - - private String _buildBrokenJsonOfLength(int len) - { - StringBuilder sb = new StringBuilder("{\"key\":"); - for (int i = 0; i < len; i++) { - sb.append("a"); - } - sb.append("!}"); - return sb.toString(); - } -} \ No newline at end of file diff --git a/src/test/java/com/fasterxml/jackson/core/ErrorReportConfigurationMaxRawContentLengthTest.java b/src/test/java/com/fasterxml/jackson/core/ErrorReportConfigurationMaxRawContentLengthTest.java deleted file mode 100644 index 999404be0d..0000000000 --- a/src/test/java/com/fasterxml/jackson/core/ErrorReportConfigurationMaxRawContentLengthTest.java +++ /dev/null @@ -1,127 +0,0 @@ -package com.fasterxml.jackson.core; - -import static org.assertj.core.api.Assertions.assertThat; -import com.fasterxml.jackson.core.io.ContentReference; - -/** - * Unit tests for class {@link ErrorReportConfiguration#getMaxRawContentLength()}. - */ -public class ErrorReportConfigurationMaxRawContentLengthTest extends BaseTest -{ - // Should be 256 - public final static int DEFAULT_LENGTH = ErrorReportConfiguration.DEFAULT_MAX_RAW_CONTENT_LENGTH; - - /* - /********************************************************** - /* Unit Tests - /********************************************************** - */ - - public void testWithJsonLocation() throws Exception - { - // Truncated result - _verifyJsonLocationToString("abc", 2, - "[Source: (String)\"ab\"[truncated 1 chars]; line: 1, column: 1]"); - // Exact length - _verifyJsonLocationToString("abc", 3, - "[Source: (String)\"abc\"; line: 1, column: 1]"); - // Enough length - _verifyJsonLocationToString("abc", 4, - "[Source: (String)\"abc\"; line: 1, column: 1]"); - } - - public void testWithJsonFactory() throws Exception - { - // default - _verifyJsonProcessingExceptionSourceLength(500, - ErrorReportConfiguration.builder().build()); - - // default - _verifyJsonProcessingExceptionSourceLength(500, - ErrorReportConfiguration.defaults()); - - // shorter - _verifyJsonProcessingExceptionSourceLength(499, - ErrorReportConfiguration.builder() - .maxRawContentLength(DEFAULT_LENGTH - 1).build()); - - // longer - _verifyJsonProcessingExceptionSourceLength(501, - ErrorReportConfiguration.builder() - .maxRawContentLength(DEFAULT_LENGTH + 1).build()); - - // zero - _verifyJsonProcessingExceptionSourceLength(0, - ErrorReportConfiguration.builder() - .maxRawContentLength(0).build()); - } - - /* - /********************************************************** - /* Internal helper methods - /********************************************************** - */ - - private void _verifyJsonLocationToString(String rawSrc, int rawContentLength, String expectedMessage) - { - ContentReference reference = _sourceRefWithErrorReportConfig(rawSrc, rawContentLength); - String location = new JsonLocation(reference, 10L, 10L, 1, 1).toString(); - assertEquals(expectedMessage, location); - } - - private ContentReference _sourceRefWithErrorReportConfig(String rawSrc, int rawContentLength) - { - return _sourceRef(rawSrc, - ErrorReportConfiguration.builder().maxRawContentLength(rawContentLength).build()); - } - - private ContentReference _sourceRef(String rawSrc, ErrorReportConfiguration errorReportConfiguration) - { - return ContentReference.construct(true, rawSrc, 0, rawSrc.length(), errorReportConfiguration); - } - - private void _verifyJsonProcessingExceptionSourceLength(int expectedRawContentLength, ErrorReportConfiguration errorReportConfiguration) - throws Exception - { - // Arrange - JsonFactory factory = streamFactoryBuilder() - .enable(StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION) - .errorReportConfiguration(errorReportConfiguration) - .build(); - // Make JSON input too long so it can be cutoff - int tooLongContent = 50 * DEFAULT_LENGTH; - String inputWithDynamicLength = _buildBrokenJsonOfLength(tooLongContent); - - // Act - try (JsonParser parser = factory.createParser(inputWithDynamicLength)) { - parser.nextToken(); - parser.nextToken(); - fail("Should not reach"); - } catch (JsonProcessingException e) { - - // Assert - String prefix = "(String)\""; - String suffix = "\"[truncated 12309 chars]"; - - // The length of the source description should be [ prefix + expected length + suffix ] - int expectedLength = prefix.length() + expectedRawContentLength + suffix.length(); - int actualLength = e.getLocation().sourceDescription().length(); - - assertEquals(expectedLength, actualLength); - assertThat(e.getMessage()) - .contains("Unrecognized token '") - .contains("was expecting (JSON"); - } - } - - private String _buildBrokenJsonOfLength(int len) - { - StringBuilder sb = new StringBuilder("{\"key\":"); - for (int i = 0; i < len; i++) { - sb.append("a"); - } - sb.append("!}"); - return sb.toString(); - } - -} diff --git a/src/test/java/com/fasterxml/jackson/core/ErrorReportConfigurationTest.java b/src/test/java/com/fasterxml/jackson/core/ErrorReportConfigurationTest.java index 4c48f02794..7a9549e55f 100644 --- a/src/test/java/com/fasterxml/jackson/core/ErrorReportConfigurationTest.java +++ b/src/test/java/com/fasterxml/jackson/core/ErrorReportConfigurationTest.java @@ -1,5 +1,8 @@ package com.fasterxml.jackson.core; +import static org.assertj.core.api.Assertions.assertThat; +import com.fasterxml.jackson.core.io.ContentReference; + /** * Unit tests for class {@link ErrorReportConfiguration}. * @@ -8,6 +11,17 @@ public class ErrorReportConfigurationTest extends BaseTest { + + /* + /********************************************************** + /* Unit Tests + /********************************************************** + */ + + private final int DEFAULT_CONTENT_LENGTH = ErrorReportConfiguration.DEFAULT_MAX_RAW_CONTENT_LENGTH; + + private final int DEFAULT_ERROR_LENGTH = ErrorReportConfiguration.DEFAULT_MAX_ERROR_TOKEN_LENGTH; + private final ErrorReportConfiguration DEFAULTS = ErrorReportConfiguration.defaults(); public void testNormalBuild() @@ -55,8 +69,8 @@ public void testInvalidMaxErrorTokenLength() public void testDefaults() { // default value - assertEquals(ErrorReportConfiguration.DEFAULT_MAX_ERROR_TOKEN_LENGTH, DEFAULTS.getMaxErrorTokenLength()); - assertEquals(ErrorReportConfiguration.DEFAULT_MAX_RAW_CONTENT_LENGTH, DEFAULTS.getMaxRawContentLength()); + assertEquals(DEFAULT_ERROR_LENGTH, DEFAULTS.getMaxErrorTokenLength()); + assertEquals(DEFAULT_CONTENT_LENGTH, DEFAULTS.getMaxRawContentLength()); // equals assertEquals(ErrorReportConfiguration.defaults(), ErrorReportConfiguration.defaults()); @@ -69,8 +83,8 @@ public void testOverrideDefaultErrorReportConfiguration() try { ErrorReportConfiguration nullDefaults = ErrorReportConfiguration.defaults(); - assertEquals(ErrorReportConfiguration.DEFAULT_MAX_ERROR_TOKEN_LENGTH, nullDefaults.getMaxErrorTokenLength()); - assertEquals(ErrorReportConfiguration.DEFAULT_MAX_RAW_CONTENT_LENGTH, nullDefaults.getMaxRawContentLength()); + assertEquals(DEFAULT_ERROR_LENGTH, nullDefaults.getMaxErrorTokenLength()); + assertEquals(DEFAULT_CONTENT_LENGTH, nullDefaults.getMaxRawContentLength()); // (2) override with other value that actually changes default values ErrorReportConfiguration.overrideDefaultErrorReportConfiguration(ErrorReportConfiguration.builder() @@ -86,8 +100,8 @@ public void testOverrideDefaultErrorReportConfiguration() // (3) revert back to default values // IMPORTANT : make sure to revert back, otherwise other tests will be affected ErrorReportConfiguration.overrideDefaultErrorReportConfiguration(ErrorReportConfiguration.builder() - .maxErrorTokenLength(ErrorReportConfiguration.DEFAULT_MAX_ERROR_TOKEN_LENGTH) - .maxRawContentLength(ErrorReportConfiguration.DEFAULT_MAX_RAW_CONTENT_LENGTH) + .maxErrorTokenLength(DEFAULT_ERROR_LENGTH) + .maxRawContentLength(DEFAULT_CONTENT_LENGTH) .build()); } } @@ -113,4 +127,187 @@ public void testBuilderConstructorWithErrorReportConfiguration() assertEquals(configA.getMaxErrorTokenLength(), configB.getMaxErrorTokenLength()); assertEquals(configA.getMaxRawContentLength(), configB.getMaxRawContentLength()); } + + public void testWithJsonLocation() throws Exception + { + // Truncated result + _verifyJsonLocationToString("abc", 2, "ab"); + // Exact length + _verifyJsonLocationToString("abc", 3, "abc"); + // Enough length + _verifyJsonLocationToString("abc", 4, "abc"); + } + + public void testWithJsonFactory() throws Exception + { + // default + _verifyJsonProcessingExceptionSourceLength(500, + ErrorReportConfiguration.builder().build()); + // default + _verifyJsonProcessingExceptionSourceLength(500, + ErrorReportConfiguration.defaults()); + // shorter + _verifyJsonProcessingExceptionSourceLength(499, + ErrorReportConfiguration.builder() + .maxRawContentLength(DEFAULT_CONTENT_LENGTH - 1).build()); + // longer + _verifyJsonProcessingExceptionSourceLength(501, + ErrorReportConfiguration.builder() + .maxRawContentLength(DEFAULT_CONTENT_LENGTH + 1).build()); + // zero + _verifyJsonProcessingExceptionSourceLength(0, + ErrorReportConfiguration.builder() + .maxRawContentLength(0).build()); + } + + public void testExpectedTokenLengthWithConfigurations() + throws Exception + { + // default + _verifyErrorTokenLength(263, + ErrorReportConfiguration.builder().build()); + // default + _verifyErrorTokenLength(263, + ErrorReportConfiguration.defaults()); + // shorter + _verifyErrorTokenLength(63, + ErrorReportConfiguration.builder() + .maxErrorTokenLength(DEFAULT_ERROR_LENGTH - 200).build()); + // longer + _verifyErrorTokenLength(463, + ErrorReportConfiguration.builder() + .maxErrorTokenLength(DEFAULT_ERROR_LENGTH + 200).build()); + // zero + _verifyErrorTokenLength(9, + ErrorReportConfiguration.builder() + .maxErrorTokenLength(0).build()); + + // negative value fails + try { + _verifyErrorTokenLength(9, + ErrorReportConfiguration.builder() + .maxErrorTokenLength(-1).build()); + } catch (IllegalArgumentException e) { + assertThat(e.getMessage()) + .contains("Value of maxErrorTokenLength") + .contains("cannot be negative"); + } + // null is not allowed, throws NPE + try { + _verifyErrorTokenLength(263, + null); + } catch (NullPointerException e) { + // no-op + } + } + + public void testNonPositiveErrorTokenConfig() + { + // Zero should be ok + ErrorReportConfiguration.builder().maxErrorTokenLength(0).build(); + + // But not -1 + try { + ErrorReportConfiguration.builder().maxErrorTokenLength(-1).build(); + fail(); + } catch (IllegalArgumentException e) { + assertThat(e.getMessage()) + .contains("Value of maxErrorTokenLength") + .contains("cannot be negative"); + } + } + + public void testNullSetterThrowsException() { + try { + newStreamFactory().setErrorReportConfiguration(null); + fail(); + } catch (NullPointerException npe) { + assertThat(npe).hasMessage("Cannot pass null ErrorReportConfiguration"); + } + } + + /* + /********************************************************** + /* Internal helper methods + /********************************************************** + */ + + private void _verifyJsonProcessingExceptionSourceLength(int expectedRawContentLength, ErrorReportConfiguration erc) + throws Exception + { + // Arrange + JsonFactory factory = streamFactoryBuilder() + .enable(StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION) + .errorReportConfiguration(erc) + .build(); + // Make JSON input too long so it can be cutoff + int tooLongContent = 50 * DEFAULT_CONTENT_LENGTH; + String inputWithDynamicLength = _buildBrokenJsonOfLength(tooLongContent); + + // Act + try (JsonParser parser = factory.createParser(inputWithDynamicLength)) { + parser.nextToken(); + parser.nextToken(); + fail("Should not reach"); + } catch (JsonProcessingException e) { + + // Assert + String prefix = "(String)\""; + String suffix = "\"[truncated 12309 chars]"; + + // The length of the source description should be [ prefix + expected length + suffix ] + int expectedLength = prefix.length() + expectedRawContentLength + suffix.length(); + int actualLength = e.getLocation().sourceDescription().length(); + + assertEquals(expectedLength, actualLength); + assertThat(e.getMessage()) + .contains("Unrecognized token '") + .contains("was expecting (JSON"); + } + } + + private void _verifyJsonLocationToString(String rawSrc, int rawContentLength, String expectedMessage) + { + ErrorReportConfiguration erc = ErrorReportConfiguration.builder() + .maxRawContentLength(rawContentLength) + .build(); + ContentReference reference = ContentReference.construct(true, rawSrc, 0, rawSrc.length(), erc); + assertEquals( + "[Source: (String)\"" + expectedMessage + "\"; line: 1, column: 1]", + new JsonLocation(reference, 10L, 10L, 1, 1).toString()); + } + + private void _verifyErrorTokenLength(int expectedTokenLen, ErrorReportConfiguration errorReportConfiguration) + throws Exception + { + JsonFactory jf3 = streamFactoryBuilder() + .errorReportConfiguration(errorReportConfiguration) + .build(); + _testWithMaxErrorTokenLength(expectedTokenLen, + // creating arbitrary number so that token reaches max len, but not over-do it + 50 * DEFAULT_ERROR_LENGTH, jf3); + } + + private void _testWithMaxErrorTokenLength(int expectedSize, int tokenLen, JsonFactory factory) + throws Exception + { + String inputWithDynamicLength = _buildBrokenJsonOfLength(tokenLen); + try (JsonParser parser = factory.createParser(inputWithDynamicLength)) { + parser.nextToken(); + parser.nextToken(); + } catch (JsonProcessingException e) { + assertThat(e.getLocation()._totalChars).isEqualTo(expectedSize); + assertThat(e.getMessage()).contains("Unrecognized token"); + } + } + + private String _buildBrokenJsonOfLength(int len) + { + StringBuilder sb = new StringBuilder("{\"key\":"); + for (int i = 0; i < len; i++) { + sb.append("a"); + } + sb.append("!}"); + return sb.toString(); + } } From ddb00d5cbe1e659c880f3ce36bdea7a0fa660f9f Mon Sep 17 00:00:00 2001 From: joohyukkim Date: Thu, 3 Aug 2023 07:43:21 +0900 Subject: [PATCH 6/6] Fix minor test failure --- .../jackson/core/ErrorReportConfigurationTest.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/test/java/com/fasterxml/jackson/core/ErrorReportConfigurationTest.java b/src/test/java/com/fasterxml/jackson/core/ErrorReportConfigurationTest.java index 7a9549e55f..956d07d070 100644 --- a/src/test/java/com/fasterxml/jackson/core/ErrorReportConfigurationTest.java +++ b/src/test/java/com/fasterxml/jackson/core/ErrorReportConfigurationTest.java @@ -131,11 +131,11 @@ public void testBuilderConstructorWithErrorReportConfiguration() public void testWithJsonLocation() throws Exception { // Truncated result - _verifyJsonLocationToString("abc", 2, "ab"); + _verifyJsonLocationToString("abc", 2, "\"ab\"[truncated 1 chars]"); // Exact length - _verifyJsonLocationToString("abc", 3, "abc"); + _verifyJsonLocationToString("abc", 3, "\"abc\""); // Enough length - _verifyJsonLocationToString("abc", 4, "abc"); + _verifyJsonLocationToString("abc", 4, "\"abc\""); } public void testWithJsonFactory() throws Exception @@ -273,7 +273,7 @@ private void _verifyJsonLocationToString(String rawSrc, int rawContentLength, St .build(); ContentReference reference = ContentReference.construct(true, rawSrc, 0, rawSrc.length(), erc); assertEquals( - "[Source: (String)\"" + expectedMessage + "\"; line: 1, column: 1]", + "[Source: (String)" + expectedMessage + "; line: 1, column: 1]", new JsonLocation(reference, 10L, 10L, 1, 1).toString()); }