diff --git a/src/main/java/com/fasterxml/jackson/core/ErrorReportConfiguration.java b/src/main/java/com/fasterxml/jackson/core/ErrorReportConfiguration.java new file mode 100644 index 0000000000..117b4be40f --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/core/ErrorReportConfiguration.java @@ -0,0 +1,191 @@ +package com.fasterxml.jackson.core; + +import java.io.Serializable; + +/** + * Container for configuration values used when handling errorneous token inputs. + * For example, unquoted text segments. + *

+ * Currently default settings are + *

+ * + * @since 2.16 + */ +public class ErrorReportConfiguration + implements Serializable { + private static final long serialVersionUID = 1L; + + /** + * Default value for {@link #_maxErrorTokenLength}. + */ + public static final int DEFAULT_MAX_ERROR_TOKEN_LENGTH = 256; + + /** + * Previous was {@link com.fasterxml.jackson.core.io.ContentReference#DEFAULT_MAX_CONTENT_SNIPPET}. + * Default value for {@link #_maxRawContentLength}. + */ + public static final int DEFAULT_MAX_RAW_CONTENT_LENGTH = 500; + + /** + * Maximum length of token to include in error messages + * + * @see Builder#maxErrorTokenLength(int) + */ + protected final int _maxErrorTokenLength; + + /** + * Maximum length of raw content to include in error messages + * + * @see Builder#maxRawContentLength(int) + */ + protected final int _maxRawContentLength; + + private static ErrorReportConfiguration DEFAULT = + new ErrorReportConfiguration(DEFAULT_MAX_ERROR_TOKEN_LENGTH, DEFAULT_MAX_RAW_CONTENT_LENGTH); + + public static void overrideDefaultErrorReportConfiguration(final ErrorReportConfiguration errorReportConfiguration) { + if (errorReportConfiguration == null) { + DEFAULT = new ErrorReportConfiguration(DEFAULT_MAX_ERROR_TOKEN_LENGTH, DEFAULT_MAX_RAW_CONTENT_LENGTH); + } else { + DEFAULT = errorReportConfiguration; + } + } + + /* + /********************************************************************** + /* Builder + /********************************************************************** + */ + + public static final class Builder { + private int maxErrorTokenLength; + private int maxRawContentLength; + + /** + * @param maxErrorTokenLength Constraints + * @return This factory instance (to allow call chaining) + * @throws IllegalArgumentException if {@code maxErrorTokenLength} is less than 0 + */ + public Builder maxErrorTokenLength(final int maxErrorTokenLength) { + validateMaxErrorTokenLength(maxErrorTokenLength); + this.maxErrorTokenLength = maxErrorTokenLength; + return this; + } + + /** + * + * @see ErrorReportConfiguration#_maxRawContentLength + * @return This factory instance (to allow call chaining) + */ + public Builder maxRawContentLength(final int maxRawContentLength) { + validateMaxRawContentLength(maxRawContentLength); + this.maxRawContentLength = maxRawContentLength; + return this; + } + + Builder() { + this(DEFAULT_MAX_ERROR_TOKEN_LENGTH, DEFAULT_MAX_RAW_CONTENT_LENGTH); + } + + Builder(final int maxErrorTokenLength, final int maxRawContentLength) { + this.maxErrorTokenLength = maxErrorTokenLength; + this.maxRawContentLength = maxRawContentLength; + } + + Builder(ErrorReportConfiguration src) { + this.maxErrorTokenLength = src._maxErrorTokenLength; + this.maxRawContentLength = src._maxRawContentLength; + } + + public ErrorReportConfiguration build() { + return new ErrorReportConfiguration(maxErrorTokenLength, maxRawContentLength); + } + } + + /* + /********************************************************************** + /* Life-cycle + /********************************************************************** + */ + + protected ErrorReportConfiguration(final int maxErrorTokenLength, final int maxRawContentLength) { + _maxErrorTokenLength = maxErrorTokenLength; + _maxRawContentLength = maxRawContentLength; + } + + public static ErrorReportConfiguration.Builder builder() { + return new ErrorReportConfiguration.Builder(); + } + + /** + * @return the default {@link ErrorReportConfiguration} (when none is set on the {@link JsonFactory} explicitly) + * @see #overrideDefaultErrorReportConfiguration(ErrorReportConfiguration) + */ + public static ErrorReportConfiguration defaults() { + return DEFAULT; + } + + /** + * @return New {@link ErrorReportConfiguration.Builder} initialized with settings of configuration + * instance + */ + public ErrorReportConfiguration.Builder rebuild() { + return new ErrorReportConfiguration.Builder(this); + } + + /* + /********************************************************************** + /* Accessors + /********************************************************************** + */ + + /** + * Accessor for {@link #_maxErrorTokenLength} + * + * @return Maximum length of token to include in error messages + * @see Builder#maxErrorTokenLength(int) + */ + public int getMaxErrorTokenLength() { + return _maxErrorTokenLength; + } + + /** + * Accessor for {@link #_maxRawContentLength} + * + * @return Maximum length of token to include in error messages + * @see Builder#maxRawContentLength + */ + public int getMaxRawContentLength() { + return _maxRawContentLength; + } + + /* + /********************************************************************** + /* Convenience methods for validation + /********************************************************************** + */ + + /** + * Convenience method that can be used verify valid {@link #_maxErrorTokenLength}. + * If invalid value is passed in, {@link IllegalArgumentException} is thrown. + * + * @param maxErrorTokenLength Maximum length of token to include in error messages + */ + private static void validateMaxErrorTokenLength(int maxErrorTokenLength) throws IllegalArgumentException { + if (maxErrorTokenLength < 0) { + throw new IllegalArgumentException( + String.format("Value of maxErrorTokenLength (%d) cannot be negative", maxErrorTokenLength)); + } + } + + private static void validateMaxRawContentLength(int maxRawContentLength) { + if (maxRawContentLength < 0) { + throw new IllegalArgumentException( + String.format("Value of maxRawContentLength (%d) cannot be negative", maxRawContentLength)); + } + } + +} diff --git a/src/main/java/com/fasterxml/jackson/core/JsonFactory.java b/src/main/java/com/fasterxml/jackson/core/JsonFactory.java index 3fa4240070..f4f0bbdca2 100644 --- a/src/main/java/com/fasterxml/jackson/core/JsonFactory.java +++ b/src/main/java/com/fasterxml/jackson/core/JsonFactory.java @@ -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. @@ -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. * @@ -2113,7 +2145,7 @@ protected IOContext _createContext(ContentReference contentRef, boolean resource contentRef = ContentReference.unknown(); } return new IOContext(_streamReadConstraints, _streamWriteConstraints, - _getBufferRecycler(), contentRef, resourceManaged); + _getBufferRecycler(), contentRef, resourceManaged, _errorReportConfiguration); } /** @@ -2131,7 +2163,7 @@ protected IOContext _createContext(Object rawContentRef, boolean resourceManaged return new IOContext(_streamReadConstraints, _streamWriteConstraints, _getBufferRecycler(), _createContentReference(rawContentRef), - resourceManaged); + resourceManaged, _errorReportConfiguration); } /** @@ -2150,7 +2182,7 @@ protected IOContext _createNonBlockingContext(Object srcRef) { return new IOContext(_streamReadConstraints, _streamWriteConstraints, _getBufferRecycler(), _createContentReference(srcRef), - false); + false, _errorReportConfiguration); } /** @@ -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); } /** @@ -2192,7 +2224,7 @@ 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, offset, length); + contentAccessor, offset, length, _errorReportConfiguration); } /* diff --git a/src/main/java/com/fasterxml/jackson/core/JsonProcessingException.java b/src/main/java/com/fasterxml/jackson/core/JsonProcessingException.java index e3364e9039..73972c7e5b 100644 --- a/src/main/java/com/fasterxml/jackson/core/JsonProcessingException.java +++ b/src/main/java/com/fasterxml/jackson/core/JsonProcessingException.java @@ -14,6 +14,9 @@ *

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

+ * Since Jackson 2.16, truncates the exception message or the raw content after length specified with configured + * {@link com.fasterxml.jackson.core.ErrorReportConfiguration} or by its defaults. */ 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..d835c5a98d 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; } @@ -323,7 +332,18 @@ public B streamReadConstraints(StreamReadConstraints 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/main/java/com/fasterxml/jackson/core/base/ParserMinimalBase.java b/src/main/java/com/fasterxml/jackson/core/base/ParserMinimalBase.java index 37b1ebef74..edb01ad554 100644 --- a/src/main/java/com/fasterxml/jackson/core/base/ParserMinimalBase.java +++ b/src/main/java/com/fasterxml/jackson/core/base/ParserMinimalBase.java @@ -128,7 +128,10 @@ public abstract class ParserMinimalBase extends JsonParser * as part of error messages. * * @since 2.9 + * @deprecated Since 2.16 + * @see ErrorReportConfiguration#getMaxErrorTokenLength() */ + @Deprecated protected final static int MAX_ERROR_TOKEN_LENGTH = 256; /* diff --git a/src/main/java/com/fasterxml/jackson/core/io/ContentReference.java b/src/main/java/com/fasterxml/jackson/core/io/ContentReference.java index 612dda5064..0c4350dc4b 100644 --- a/src/main/java/com/fasterxml/jackson/core/io/ContentReference.java +++ b/src/main/java/com/fasterxml/jackson/core/io/ContentReference.java @@ -1,5 +1,7 @@ package com.fasterxml.jackson.core.io; +import com.fasterxml.jackson.core.ErrorReportConfiguration; + import java.io.File; import java.io.IOException; import java.io.ObjectInputStream; @@ -52,6 +54,9 @@ public class ContentReference * logs. * * @since 2.9 + * @deprecated Since 2.16. {@link ErrorReportConfiguration#DEFAULT_MAX_RAW_CONTENT_LENGTH} will be used by default or + * as configured by {@link ErrorReportConfiguration.Builder#maxRawContentLength(int)} + * will be used instead. */ public static final int DEFAULT_MAX_CONTENT_SNIPPET = 500; @@ -81,6 +86,13 @@ public class ContentReference */ protected final boolean _isContentTextual; + /** + * max raw content to return as configured + * + * @since 2.16 + */ + protected final int _maxRawContentLength; + /* /********************************************************************** /* Life-cycle @@ -91,13 +103,31 @@ protected ContentReference(boolean isContentTextual, Object rawContent) { this(isContentTextual, rawContent, -1, -1); } + /** + * @deprecated Since 2.16. Use {@link #ContentReference(boolean, Object, int, int, ErrorReportConfiguration)} instead. + */ protected ContentReference(boolean isContentTextual, Object rawContent, int offset, int length) + { + this(isContentTextual, rawContent, offset, length, ErrorReportConfiguration.defaults()); + } + + protected ContentReference(boolean isContentTextual, Object rawContent, + int offset, int length, ErrorReportConfiguration errorReportConfiguration) { _isContentTextual = isContentTextual; _rawContent = rawContent; _offset = offset; _length = length; + _maxRawContentLength = errorReportConfiguration.getMaxRawContentLength(); + } + + /** + * @since 2.16 + */ + public ContentReference(boolean isContentTextual, Object rawContent, + ErrorReportConfiguration errorReportConfiguration) { + this(isContentTextual, rawContent, -1, -1, errorReportConfiguration); } /** @@ -131,7 +161,23 @@ public static ContentReference construct(boolean isContentTextual, Object rawCon public static ContentReference construct(boolean isContentTextual, Object rawContent, int offset, int length) { - return new ContentReference(isContentTextual, rawContent, offset, length); + return new ContentReference(isContentTextual, rawContent, offset, length, ErrorReportConfiguration.defaults()); + } + + /** + * @since 2.16 + */ + public static ContentReference construct(boolean isContentTextual, Object rawContent, + int offset, int length, ErrorReportConfiguration errorReportConfiguration) + { + return new ContentReference(isContentTextual, rawContent, offset, length, errorReportConfiguration); + } + + /** + * @since 2.16 + */ + public static ContentReference construct(boolean isContentTextual, Object rawContent, ErrorReportConfiguration errorReportConfiguration) { + return new ContentReference(isContentTextual, rawContent, errorReportConfiguration); } /** @@ -203,10 +249,11 @@ public Object getRawContent() { * which content is counted, either bytes or chars) to use for truncation * (so as not to include full content for humongous sources or targets) * + * @see ErrorReportConfiguration#builder()#_maxRawContentLength * @return Maximum content snippet to include before truncating */ protected int maxContentSnippetLength() { - return DEFAULT_MAX_CONTENT_SNIPPET; + return _maxRawContentLength; } /* diff --git a/src/main/java/com/fasterxml/jackson/core/io/IOContext.java b/src/main/java/com/fasterxml/jackson/core/io/IOContext.java index 203318c3f3..a378254884 100644 --- a/src/main/java/com/fasterxml/jackson/core/io/IOContext.java +++ b/src/main/java/com/fasterxml/jackson/core/io/IOContext.java @@ -1,11 +1,13 @@ package com.fasterxml.jackson.core.io; +import com.fasterxml.jackson.core.ErrorReportConfiguration; import com.fasterxml.jackson.core.JsonEncoding; import com.fasterxml.jackson.core.StreamReadConstraints; import com.fasterxml.jackson.core.StreamWriteConstraints; import com.fasterxml.jackson.core.util.BufferRecycler; import com.fasterxml.jackson.core.util.ReadConstrainedTextBuffer; import com.fasterxml.jackson.core.util.TextBuffer; +import java.util.Objects; /** * To limit number of configuration and state objects to pass, all @@ -68,6 +70,12 @@ public class IOContext */ protected final StreamReadConstraints _streamReadConstraints; + /** + * @see ErrorReportConfiguration + * @since 2.16 + */ + protected final ErrorReportConfiguration _errorReportConfiguration; + /** * @since 2.16 */ @@ -131,7 +139,7 @@ public class IOContext * @since 2.16 */ public IOContext(StreamReadConstraints src, StreamWriteConstraints swc, BufferRecycler br, - ContentReference contentRef, boolean managedResource) + ContentReference contentRef, boolean managedResource, ErrorReportConfiguration errorReportConfiguration) { _streamReadConstraints = (src == null) ? StreamReadConstraints.defaults() : src; @@ -141,6 +149,7 @@ public IOContext(StreamReadConstraints src, StreamWriteConstraints swc, BufferRe _contentReference = contentRef; _sourceRef = contentRef.getRawContent(); _managedResource = managedResource; + _errorReportConfiguration = errorReportConfiguration; } /** @@ -163,6 +172,7 @@ public IOContext(StreamReadConstraints src, BufferRecycler br, _contentReference = contentRef; _sourceRef = contentRef.getRawContent(); _managedResource = managedResource; + _errorReportConfiguration = ErrorReportConfiguration.defaults(); } /** @@ -175,7 +185,8 @@ public IOContext(StreamReadConstraints src, BufferRecycler br, @Deprecated // since 2.15 public IOContext(BufferRecycler br, ContentReference contentRef, boolean managedResource) { - this(null, null, br, contentRef, managedResource); + this(StreamReadConstraints.defaults(), StreamWriteConstraints.defaults(), + br, contentRef, managedResource, ErrorReportConfiguration.defaults()); } @Deprecated // since 2.13 @@ -190,6 +201,16 @@ public IOContext(BufferRecycler br, Object rawContent, boolean managedResource) public StreamReadConstraints streamReadConstraints() { return _streamReadConstraints; } + + /** + * Returns : {@link ErrorReportConfiguration}, container for configuration values used when + * handling errorneous token inputs. + * + * @since 2.16 + */ + public ErrorReportConfiguration errorReportConfiguration() { + return _errorReportConfiguration; + } /** * @return constraints for streaming writes @@ -220,6 +241,7 @@ public IOContext withEncoding(JsonEncoding enc) { /** * Accessor for getting (some) information about input source, mostly * usable for error reporting purposes. + * This MUST only be called if {@link com.fasterxml.jackson.core.StreamReadFeature#INCLUDE_SOURCE_IN_LOCATION} is enabled. * * @return Reference to input source * diff --git a/src/main/java/com/fasterxml/jackson/core/json/ReaderBasedJsonParser.java b/src/main/java/com/fasterxml/jackson/core/json/ReaderBasedJsonParser.java index ccbfa7d2fe..22aedb5ae9 100644 --- a/src/main/java/com/fasterxml/jackson/core/json/ReaderBasedJsonParser.java +++ b/src/main/java/com/fasterxml/jackson/core/json/ReaderBasedJsonParser.java @@ -3033,7 +3033,7 @@ protected void _reportInvalidToken(String matchedPart, String msg) throws IOExce } ++_inputPtr; sb.append(c); - if (sb.length() >= MAX_ERROR_TOKEN_LENGTH) { + if (sb.length() >= _ioContext.errorReportConfiguration().getMaxErrorTokenLength()) { sb.append("..."); break; } diff --git a/src/main/java/com/fasterxml/jackson/core/json/UTF8StreamJsonParser.java b/src/main/java/com/fasterxml/jackson/core/json/UTF8StreamJsonParser.java index 48d71b6af7..125e9c530c 100644 --- a/src/main/java/com/fasterxml/jackson/core/json/UTF8StreamJsonParser.java +++ b/src/main/java/com/fasterxml/jackson/core/json/UTF8StreamJsonParser.java @@ -3691,7 +3691,7 @@ protected void _reportInvalidToken(String matchedPart, String msg) throws IOExce break; } sb.append(c); - if (sb.length() >= MAX_ERROR_TOKEN_LENGTH) { + if (sb.length() >= _ioContext.errorReportConfiguration().getMaxErrorTokenLength()) { sb.append("..."); break; } diff --git a/src/main/java/com/fasterxml/jackson/core/json/async/NonBlockingUtf8JsonParserBase.java b/src/main/java/com/fasterxml/jackson/core/json/async/NonBlockingUtf8JsonParserBase.java index d5a962e556..e581fb663d 100644 --- a/src/main/java/com/fasterxml/jackson/core/json/async/NonBlockingUtf8JsonParserBase.java +++ b/src/main/java/com/fasterxml/jackson/core/json/async/NonBlockingUtf8JsonParserBase.java @@ -1244,7 +1244,7 @@ protected JsonToken _finishErrorToken() throws IOException // 11-Jan-2016, tatu: note: we will fully consume the character, // included or not, so if recovery was possible, it'd be off-by-one... _textBuffer.append(ch); - if (_textBuffer.size() < MAX_ERROR_TOKEN_LENGTH) { + if (_textBuffer.size() < _ioContext.errorReportConfiguration().getMaxErrorTokenLength()) { continue; } } 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..730da89d59 --- /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(); + } +} diff --git a/src/test/java/com/fasterxml/jackson/core/ErrorReportConfigurationMaxRawContentLengthTest.java b/src/test/java/com/fasterxml/jackson/core/ErrorReportConfigurationMaxRawContentLengthTest.java new file mode 100644 index 0000000000..2f865b5aaf --- /dev/null +++ b/src/test/java/com/fasterxml/jackson/core/ErrorReportConfigurationMaxRawContentLengthTest.java @@ -0,0 +1,48 @@ +package com.fasterxml.jackson.core; + +import com.fasterxml.jackson.core.io.ContentReference; + +/** + * Unit tests for class {@link ErrorReportConfiguration#getMaxRawContentLength()}. + */ +public class ErrorReportConfigurationMaxRawContentLengthTest extends BaseTest { + /* + /********************************************************** + /* Unit Tests + /********************************************************** + */ + + public void testBasicToStringErrorConfig() throws Exception { + // Truncated result + _verifyToString("abc", 2, + "[Source: (String)\"ab\"[truncated 1 chars]; line: 1, column: 1]"); + // Exact length + _verifyToString("abc", 3, + "[Source: (String)\"abc\"; line: 1, column: 1]"); + // Enough length + _verifyToString("abc", 4, + "[Source: (String)\"abc\"; line: 1, column: 1]"); + } + + /* + /********************************************************** + /* Internal helper methods + /********************************************************** + */ + + private void _verifyToString(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); + } + +} diff --git a/src/test/java/com/fasterxml/jackson/core/ErrorReportConfigurationTest.java b/src/test/java/com/fasterxml/jackson/core/ErrorReportConfigurationTest.java new file mode 100644 index 0000000000..49b826910d --- /dev/null +++ b/src/test/java/com/fasterxml/jackson/core/ErrorReportConfigurationTest.java @@ -0,0 +1,10 @@ +package com.fasterxml.jackson.core; + +public class ErrorReportConfigurationTest extends BaseTest { + + public void testDefaults() { + assertEquals( + ErrorReportConfiguration.defaults(), + ErrorReportConfiguration.defaults()); + } +} diff --git a/src/test/java/com/fasterxml/jackson/core/TestVersions.java b/src/test/java/com/fasterxml/jackson/core/TestVersions.java index 2b2e1abdf4..7d96b203b6 100644 --- a/src/test/java/com/fasterxml/jackson/core/TestVersions.java +++ b/src/test/java/com/fasterxml/jackson/core/TestVersions.java @@ -49,6 +49,7 @@ private void assertVersion(Version v) private IOContext getIOContext() { return new IOContext(StreamReadConstraints.defaults(), StreamWriteConstraints.defaults(), - new BufferRecycler(), ContentReference.unknown(), false); + new BufferRecycler(), ContentReference.unknown(), false, + ErrorReportConfiguration.defaults()); } } diff --git a/src/test/java/com/fasterxml/jackson/core/io/TestMergedStream.java b/src/test/java/com/fasterxml/jackson/core/io/TestMergedStream.java index 8d9610c47f..94aa63c7ad 100644 --- a/src/test/java/com/fasterxml/jackson/core/io/TestMergedStream.java +++ b/src/test/java/com/fasterxml/jackson/core/io/TestMergedStream.java @@ -1,5 +1,6 @@ package com.fasterxml.jackson.core.io; +import com.fasterxml.jackson.core.ErrorReportConfiguration; import java.io.*; import com.fasterxml.jackson.core.JsonEncoding; @@ -15,7 +16,8 @@ public void testSimple() throws Exception BufferRecycler rec = new BufferRecycler(); IOContext ctxt = new IOContext(StreamReadConstraints.defaults(), StreamWriteConstraints.defaults(), - rec, ContentReference.UNKNOWN_CONTENT, false); + rec, ContentReference.UNKNOWN_CONTENT, false, + ErrorReportConfiguration.defaults()); // bit complicated; must use recyclable buffer... byte[] first = ctxt.allocReadIOBuffer(); System.arraycopy("ABCDE".getBytes("UTF-8"), 0, first, 99, 5); diff --git a/src/test/java/com/fasterxml/jackson/core/util/ReadConstrainedTextBufferTest.java b/src/test/java/com/fasterxml/jackson/core/util/ReadConstrainedTextBufferTest.java index cf83409123..82655290fa 100644 --- a/src/test/java/com/fasterxml/jackson/core/util/ReadConstrainedTextBufferTest.java +++ b/src/test/java/com/fasterxml/jackson/core/util/ReadConstrainedTextBufferTest.java @@ -1,5 +1,6 @@ package com.fasterxml.jackson.core.util; +import com.fasterxml.jackson.core.ErrorReportConfiguration; import com.fasterxml.jackson.core.StreamReadConstraints; import com.fasterxml.jackson.core.StreamWriteConstraints; import com.fasterxml.jackson.core.io.ContentReference; @@ -48,7 +49,8 @@ private static TextBuffer makeConstrainedBuffer(int maxStringLen) { constraints, StreamWriteConstraints.defaults(), new BufferRecycler(), - ContentReference.rawReference("N/A"), true); + ContentReference.rawReference("N/A"), true, + ErrorReportConfiguration.defaults()); return ioContext.constructReadConstrainedTextBuffer(); } -} \ No newline at end of file +} diff --git a/src/test/java/com/fasterxml/jackson/core/write/UTF8GeneratorTest.java b/src/test/java/com/fasterxml/jackson/core/write/UTF8GeneratorTest.java index 1e83778040..09dffd4a2f 100644 --- a/src/test/java/com/fasterxml/jackson/core/write/UTF8GeneratorTest.java +++ b/src/test/java/com/fasterxml/jackson/core/write/UTF8GeneratorTest.java @@ -50,7 +50,7 @@ public void testNestingDepthWithSmallLimit() throws Exception IOContext ioc = new IOContext(null, StreamWriteConstraints.builder().maxNestingDepth(1).build(), new BufferRecycler(), - ContentReference.rawReference(bytes), true); + ContentReference.rawReference(bytes), true, ErrorReportConfiguration.defaults()); try (JsonGenerator gen = new UTF8JsonGenerator(ioc, 0, null, bytes, '"')) { gen.writeStartObject(); gen.writeFieldName("array"); @@ -68,7 +68,7 @@ public void testNestingDepthWithSmallLimitNestedObject() throws Exception IOContext ioc = new IOContext(null, StreamWriteConstraints.builder().maxNestingDepth(1).build(), new BufferRecycler(), - ContentReference.rawReference(bytes), true); + ContentReference.rawReference(bytes), true, ErrorReportConfiguration.defaults()); try (JsonGenerator gen = new UTF8JsonGenerator(ioc, 0, null, bytes, '"')) { gen.writeStartObject(); gen.writeFieldName("object"); diff --git a/src/test/java/com/fasterxml/jackson/core/write/WriterBasedJsonGeneratorTest.java b/src/test/java/com/fasterxml/jackson/core/write/WriterBasedJsonGeneratorTest.java index 3f4eb63ec1..d5e18474c8 100644 --- a/src/test/java/com/fasterxml/jackson/core/write/WriterBasedJsonGeneratorTest.java +++ b/src/test/java/com/fasterxml/jackson/core/write/WriterBasedJsonGeneratorTest.java @@ -1,6 +1,7 @@ package com.fasterxml.jackson.core.write; import com.fasterxml.jackson.core.BaseTest; +import com.fasterxml.jackson.core.ErrorReportConfiguration; import com.fasterxml.jackson.core.JsonFactory; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.StreamWriteConstraints; @@ -22,7 +23,8 @@ public void testNestingDepthWithSmallLimit() throws Exception IOContext ioc = new IOContext(null, StreamWriteConstraints.builder().maxNestingDepth(1).build(), new BufferRecycler(), - ContentReference.rawReference(sw), true); + ContentReference.rawReference(sw), true, + ErrorReportConfiguration.defaults()); try (JsonGenerator gen = new WriterBasedJsonGenerator(ioc, 0, null, sw, '"')) { gen.writeStartObject(); gen.writeFieldName("array"); @@ -40,7 +42,8 @@ public void testNestingDepthWithSmallLimitNestedObject() throws Exception IOContext ioc = new IOContext(null, StreamWriteConstraints.builder().maxNestingDepth(1).build(), new BufferRecycler(), - ContentReference.rawReference(sw), true); + ContentReference.rawReference(sw), true, + ErrorReportConfiguration.defaults()); try (JsonGenerator gen = new WriterBasedJsonGenerator(ioc, 0, null, sw, '"')) { gen.writeStartObject(); gen.writeFieldName("object");