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
+ *
+ * - Maximum length of token to include in error messages (see {@link #_maxErrorTokenLength})
+ *
- Maximum length of raw content to include in error messages (see {@link #_maxRawContentLength})
+ *
+ *
+ * @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");