diff --git a/core/pom.xml b/core/pom.xml
index a3473dbbd..b8f454143 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -69,7 +69,7 @@
org.everit.json.schema;version=${project.version},
org.everit.json.schema.loader;version=${project.version},
- org.everit.json.schema.regexp;version=${project.version}
+ org.everit.json.schema.regexp;version=${project.version},
org.everit.json.schema.event;version=${project.version}
diff --git a/core/src/main/java/org/everit/json/schema/ConditionalSchemaValidatingVisitor.java b/core/src/main/java/org/everit/json/schema/ConditionalSchemaValidatingVisitor.java
index ff947e494..46cb72bd1 100644
--- a/core/src/main/java/org/everit/json/schema/ConditionalSchemaValidatingVisitor.java
+++ b/core/src/main/java/org/everit/json/schema/ConditionalSchemaValidatingVisitor.java
@@ -52,7 +52,7 @@ void visitThenSchema(Schema thenSchema) {
if (ifSchemaException == null) {
ValidationException thenSchemaException = owner.getFailureOfSchema(thenSchema, subject);
if (thenSchemaException != null) {
- ValidationException failure = new ValidationException(conditionalSchema,
+ ValidationException failure = new InternalValidationException(conditionalSchema,
new StringBuilder(new StringBuilder("#")),
"input is invalid against the \"then\" schema",
asList(thenSchemaException),
@@ -72,7 +72,7 @@ void visitElseSchema(Schema elseSchema) {
if (ifSchemaException != null) {
ValidationException elseSchemaException = owner.getFailureOfSchema(elseSchema, subject);
if (elseSchemaException != null) {
- ValidationException failure = new ValidationException(conditionalSchema,
+ ValidationException failure = new InternalValidationException(conditionalSchema,
new StringBuilder(new StringBuilder("#")),
"input is invalid against both the \"if\" and \"else\" schema",
asList(ifSchemaException, elseSchemaException),
diff --git a/core/src/main/java/org/everit/json/schema/InternalValidationException.java b/core/src/main/java/org/everit/json/schema/InternalValidationException.java
new file mode 100644
index 000000000..034d2cc8d
--- /dev/null
+++ b/core/src/main/java/org/everit/json/schema/InternalValidationException.java
@@ -0,0 +1,30 @@
+package org.everit.json.schema;
+
+import java.util.List;
+
+class InternalValidationException extends ValidationException {
+
+ InternalValidationException(Schema violatedSchema, Class> expectedType, Object actualValue) {
+ super(violatedSchema, expectedType, actualValue);
+ }
+
+ InternalValidationException(Schema violatedSchema, Class> expectedType, Object actualValue, String keyword,
+ String schemaLocation) {
+ super(violatedSchema, expectedType, actualValue, keyword, schemaLocation);
+ }
+
+ InternalValidationException(Schema violatedSchema, String message, String keyword, String schemaLocation) {
+ super(violatedSchema, message, keyword, schemaLocation);
+ }
+
+ InternalValidationException(Schema violatedSchema, StringBuilder pointerToViolation, String message,
+ List causingExceptions, String keyword, String schemaLocation) {
+ super(violatedSchema, pointerToViolation, message, causingExceptions, keyword, schemaLocation);
+ }
+
+ @Override
+ public synchronized Throwable fillInStackTrace() {
+ return this;
+ }
+
+}
diff --git a/core/src/main/java/org/everit/json/schema/ValidatingVisitor.java b/core/src/main/java/org/everit/json/schema/ValidatingVisitor.java
index 5b7b419df..77e088522 100644
--- a/core/src/main/java/org/everit/json/schema/ValidatingVisitor.java
+++ b/core/src/main/java/org/everit/json/schema/ValidatingVisitor.java
@@ -164,7 +164,7 @@ void visitCombinedSchema(CombinedSchema combinedSchema) {
try {
criterion.validate(subschemas.size(), matchingCount);
} catch (ValidationException e) {
- failureReporter.failure(new ValidationException(combinedSchema,
+ failureReporter.failure(new InternalValidationException(combinedSchema,
new StringBuilder(e.getPointerToViolation()),
e.getMessage(),
failures,
diff --git a/core/src/main/java/org/everit/json/schema/ValidationException.java b/core/src/main/java/org/everit/json/schema/ValidationException.java
index e5eb78a0e..8ec82c3b8 100644
--- a/core/src/main/java/org/everit/json/schema/ValidationException.java
+++ b/core/src/main/java/org/everit/json/schema/ValidationException.java
@@ -20,6 +20,8 @@
public class ValidationException extends RuntimeException {
private static final long serialVersionUID = 6192047123024651924L;
+
+
private static int getViolationCount(List causes) {
int causeCount = causes.stream().mapToInt(ValidationException::getViolationCount).sum();
return Math.max(1, causeCount);
@@ -67,7 +69,7 @@ public static void throwFor(Schema rootFailingSchema,
}
static ValidationException createWrappingException(Schema rootFailingSchema, List failures) {
- return new ValidationException(rootFailingSchema,
+ return new InternalValidationException(rootFailingSchema,
new StringBuilder("#"),
getViolationCount(failures) + " schema violations found",
new ArrayList<>(failures),
@@ -393,7 +395,7 @@ public ValidationException prepend(String fragment, Schema violatedSchema) {
List prependedCausingExceptions = causingExceptions.stream()
.map(exc -> exc.prepend(escapedFragment))
.collect(Collectors.toList());
- return new ValidationException(newPointer, violatedSchema, super.getMessage(),
+ return new InternalValidationException(violatedSchema, newPointer, super.getMessage(),
prependedCausingExceptions, this.keyword, this.schemaLocation);
}
@@ -479,4 +481,9 @@ public String getSchemaLocation() {
result = 31 * result + (keyword == null ? 0 : keyword.hashCode());
return result;
}
+
+ ValidationException copy() {
+ return new ValidationException(pointerToViolation, violatedSchema, super.getMessage(), causingExceptions,
+ keyword, schemaLocation);
+ }
}
diff --git a/core/src/main/java/org/everit/json/schema/ValidationFailureReporter.java b/core/src/main/java/org/everit/json/schema/ValidationFailureReporter.java
index aa2234a0d..0de6a65e5 100644
--- a/core/src/main/java/org/everit/json/schema/ValidationFailureReporter.java
+++ b/core/src/main/java/org/everit/json/schema/ValidationFailureReporter.java
@@ -18,11 +18,11 @@ abstract class ValidationFailureReporter {
}
void failure(String message, String keyword) {
- failure(new ValidationException(schema, message, keyword, schema.getSchemaLocation()));
+ failure(new InternalValidationException(schema, message, keyword, schema.getSchemaLocation()));
}
void failure(Class> expectedType, Object actualValue) {
- failure(new ValidationException(schema, expectedType, actualValue, "type", schema.getSchemaLocation()));
+ failure(new InternalValidationException(schema, expectedType, actualValue, "type", schema.getSchemaLocation()));
}
abstract void failure(ValidationException exc);
diff --git a/core/src/main/java/org/everit/json/schema/Validator.java b/core/src/main/java/org/everit/json/schema/Validator.java
index 5bcf8d852..1846d6d35 100644
--- a/core/src/main/java/org/everit/json/schema/Validator.java
+++ b/core/src/main/java/org/everit/json/schema/Validator.java
@@ -66,8 +66,12 @@ class DefaultValidator implements Validator {
ValidationFailureReporter failureReporter = createFailureReporter(schema);
ReadWriteValidator readWriteValidator = ReadWriteValidator.createForContext(readWriteContext, failureReporter);
ValidatingVisitor visitor = new ValidatingVisitor(input, failureReporter, readWriteValidator, validationListener);
- visitor.visit(schema);
- visitor.failIfErrorFound();
+ try {
+ visitor.visit(schema);
+ visitor.failIfErrorFound();
+ } catch (InternalValidationException e) {
+ throw e.copy();
+ }
}
private ValidationFailureReporter createFailureReporter(Schema schema) {
diff --git a/core/src/test/java/org/everit/json/schema/ConditionalSchemaEventsTest.java b/core/src/test/java/org/everit/json/schema/ConditionalSchemaEventsTest.java
index d950f8340..2bab98a02 100644
--- a/core/src/test/java/org/everit/json/schema/ConditionalSchemaEventsTest.java
+++ b/core/src/test/java/org/everit/json/schema/ConditionalSchemaEventsTest.java
@@ -60,7 +60,7 @@ public void ifMatch_thenMismatch() {
validateInstance(instance);
verify(listener).ifSchemaMatch(new ConditionalSchemaMatchEvent(schema, instance, IF));
- ValidationException failure = new ValidationException(MIN_LENGTH_STRING_SCHEMA,
+ ValidationException failure = new InternalValidationException(MIN_LENGTH_STRING_SCHEMA,
"expected minLength: 6, actual: 3", "minLength",
"#/then");
verify(listener).thenSchemaMismatch(new ConditionalSchemaMismatchEvent(schema, instance, THEN, failure));
@@ -72,8 +72,8 @@ public void ifMismatch_elseMatch() {
String instance = "boo";
validateInstance(instance);
- ValidationException failure = new ValidationException(PATTERN_STRING_SCHEMA, "string [boo] does not match pattern f.*o",
- "pattern", "#/if");
+ ValidationException failure = new InternalValidationException(PATTERN_STRING_SCHEMA,
+ "string [boo] does not match pattern f.*o", "pattern", "#/if");
verify(listener).ifSchemaMismatch(new ConditionalSchemaMismatchEvent(schema, instance, IF, failure));
verify(listener).elseSchemaMatch(new ConditionalSchemaMatchEvent(schema, instance, ELSE));
verifyNoMoreInteractions(listener);
@@ -84,11 +84,11 @@ public void ifMismatch_elseMismatch() {
String instance = "booooooooooooo";
validateInstance(instance);
- ValidationException ifFailure = new ValidationException(PATTERN_STRING_SCHEMA,
+ ValidationException ifFailure = new InternalValidationException(PATTERN_STRING_SCHEMA,
"string [booooooooooooo] does not match pattern f.*o",
"pattern", "#/if");
verify(listener).ifSchemaMismatch(new ConditionalSchemaMismatchEvent(schema, instance, IF, ifFailure));
- ValidationException elseFailure = new ValidationException(MAX_LENGTH_STRING_SCHEMA,
+ ValidationException elseFailure = new InternalValidationException(MAX_LENGTH_STRING_SCHEMA,
"expected maxLength: 4, actual: 14",
"maxLength", "#/else");
verify(listener).elseSchemaMismatch(new ConditionalSchemaMismatchEvent(schema, instance, ELSE, elseFailure));
diff --git a/core/src/test/java/org/everit/json/schema/InternalValidationExceptionTest.java b/core/src/test/java/org/everit/json/schema/InternalValidationExceptionTest.java
new file mode 100644
index 000000000..dec5c767c
--- /dev/null
+++ b/core/src/test/java/org/everit/json/schema/InternalValidationExceptionTest.java
@@ -0,0 +1,17 @@
+package org.everit.json.schema;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+
+public class InternalValidationExceptionTest {
+
+ @Test
+ public void stackTraceShouldBeEmpty() {
+ try {
+ throw new InternalValidationException(BooleanSchema.INSTANCE, "message", "keyword", "#");
+ } catch (ValidationException e) {
+ assertEquals(0, e.getStackTrace().length);
+ }
+ }
+}
diff --git a/core/src/test/java/org/everit/json/schema/TestSupport.java b/core/src/test/java/org/everit/json/schema/TestSupport.java
index 6f881a6dc..42274db23 100644
--- a/core/src/test/java/org/everit/json/schema/TestSupport.java
+++ b/core/src/test/java/org/everit/json/schema/TestSupport.java
@@ -17,6 +17,7 @@
import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
@@ -24,6 +25,7 @@
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.List;
+import java.util.Optional;
import org.everit.json.schema.loader.SchemaLoader;
@@ -163,6 +165,7 @@ public static void expectFailure(final Schema failingSchema,
test(failingSchema, expectedPointer, input);
} catch (ValidationException e) {
assertSame(expectedViolatedSchema, e.getViolatedSchema());
+ verifyStacktraces(e);
}
}
@@ -185,9 +188,28 @@ public static void expectFailure(final Failure failure) {
if (failure.expectedMessageFragment != null) {
assertThat(e.getMessage(), containsString(failure.expectedMessageFragment));
}
+ verifyStacktraces(e);
}
}
+ private static void verifyStacktraces(ValidationException e) {
+ assertNotEquals(0, e.getStackTrace().length);
+ assertEmptyCauseStackTraces(e).ifPresent(nonempty -> {
+ throw new AssertionError("non-empty stacktrace: " + nonempty);
+ });
+ }
+
+ private static Optional assertEmptyCauseStackTraces(ValidationException e) {
+ return e.getCausingExceptions().stream().filter(exc -> exc.getStackTrace().length > 0)
+ .findFirst()
+ .map(Optional::of)
+ .orElseGet(() -> e.getCausingExceptions().stream()
+ .map(TestSupport::assertEmptyCauseStackTraces)
+ .filter(Optional::isPresent)
+ .findFirst()
+ .orElse(Optional.empty()));
+ }
+
public static final InputStream asStream(final String string) {
return new ByteArrayInputStream(string.getBytes());
}
diff --git a/core/src/test/java/org/everit/json/schema/ValidatingVisitorTest.java b/core/src/test/java/org/everit/json/schema/ValidatingVisitorTest.java
index 30581cd56..181b3a259 100644
--- a/core/src/test/java/org/everit/json/schema/ValidatingVisitorTest.java
+++ b/core/src/test/java/org/everit/json/schema/ValidatingVisitorTest.java
@@ -133,7 +133,7 @@ public void triggersCombinedSchemaEvents() {
new ValidatingVisitor(instance, reporter, ReadWriteValidator.NONE, listener).visit(combinedSchema);
- ValidationException exc = new ValidationException(stringSchema, String.class, instance);
+ ValidationException exc = new InternalValidationException(stringSchema, String.class, instance);
verify(listener).combinedSchemaMismatch(new CombinedSchemaMismatchEvent(combinedSchema, stringSchema, instance, exc));
verify(listener).combinedSchemaMatch(new CombinedSchemaMatchEvent(combinedSchema, emptySchema, instance));
verify(listener).combinedSchemaMatch(new CombinedSchemaMatchEvent(combinedSchema, objectSchema, instance));
diff --git a/core/src/test/java/org/everit/json/schema/ValidationExceptionTest.java b/core/src/test/java/org/everit/json/schema/ValidationExceptionTest.java
index cc42db34e..3bab5e032 100644
--- a/core/src/test/java/org/everit/json/schema/ValidationExceptionTest.java
+++ b/core/src/test/java/org/everit/json/schema/ValidationExceptionTest.java
@@ -15,6 +15,7 @@
*/
package org.everit.json.schema;
+import static java.util.Collections.emptyList;
import static org.everit.json.schema.JSONMatcher.sameJsonAs;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
@@ -40,7 +41,7 @@ public void constructorNullSchema() {
private ValidationException createDummyException(final String pointer) {
return new ValidationException(BooleanSchema.INSTANCE,
new StringBuilder(pointer),
- "stuff went wrong", Collections.emptyList());
+ "stuff went wrong", emptyList());
}
@Test
@@ -112,7 +113,7 @@ public void prependWithCausingExceptions() {
private ValidationException subjectWithCauses(final ValidationException... causes) {
if (causes.length == 0) {
- return new ValidationException("");
+ return new ValidationException(rootSchema, "", emptyList());
}
try {
ValidationException.throwFor(rootSchema, Arrays.asList(causes));
@@ -169,7 +170,7 @@ public void throwForMultipleFailures() {
@Test
public void throwForNoFailure() {
- ValidationException.throwFor(rootSchema, Collections.emptyList());
+ ValidationException.throwFor(rootSchema, emptyList());
}
@Test
@@ -195,7 +196,7 @@ public void toStringWithCauses() {
public void testToJSON() {
ValidationException subject =
new ValidationException(BooleanSchema.INSTANCE, new StringBuilder("#/a/b"),
- "exception message", Collections.emptyList(), "type", null);
+ "exception message", emptyList(), "type", null);
JSONObject expected = loader.readObj("exception-to-json.json");
JSONObject actual = subject.toJSON();
assertThat(actual, sameJsonAs(expected));
@@ -205,7 +206,7 @@ public void testToJSON() {
public void testToJSONWithSchemaLocation() {
ValidationException subject =
new ValidationException(BooleanSchema.INSTANCE, new StringBuilder("#/a/b"),
- "exception message", Collections.emptyList(), "type", "#/schema/location");
+ "exception message", emptyList(), "type", "#/schema/location");
JSONObject expected = loader.readObj("exception-to-json-with-schema-location.json");
JSONObject actual = subject.toJSON();
assertThat(actual, sameJsonAs(expected));
@@ -215,7 +216,7 @@ public void testToJSONWithSchemaLocation() {
public void toJSONNullPointerToViolation() {
ValidationException subject =
new ValidationException(BooleanSchema.INSTANCE, null,
- "exception message", Collections.emptyList(), "type", null);
+ "exception message", emptyList(), "type", null);
JSONObject actual = subject.toJSON();
assertEquals(JSONObject.NULL, actual.get("pointerToViolation"));
}
@@ -226,7 +227,7 @@ public void toJSONWithCauses() {
new ValidationException(NullSchema.INSTANCE,
new StringBuilder("#/a/0"),
"cause msg",
- Collections.emptyList(),
+ emptyList(),
"type",
null);
ValidationException subject =
@@ -237,4 +238,11 @@ public void toJSONWithCauses() {
assertThat(actual, sameJsonAs(expected));
}
+ @Test
+ public void testCopy() {
+ ValidationException subject = subjectWithCauses(subjectWithCauses());
+ ValidationException copy = subject.copy();
+ assertEquals(subject, copy);
+ }
+
}
diff --git a/tests/vanilla/src/main/java/org/everit/json/schema/TestCase.java b/tests/vanilla/src/main/java/org/everit/json/schema/TestCase.java
index 8f4bbf73a..db8dd92c8 100644
--- a/tests/vanilla/src/main/java/org/everit/json/schema/TestCase.java
+++ b/tests/vanilla/src/main/java/org/everit/json/schema/TestCase.java
@@ -1,11 +1,14 @@
package org.everit.json.schema;
+import static org.junit.Assert.assertNotEquals;
+
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UncheckedIOException;
import java.util.ArrayList;
import java.util.List;
+import java.util.Optional;
import java.util.Set;
import java.util.regex.Pattern;
@@ -89,9 +92,28 @@ private void testWithValidator(Validator validator, Schema schema) {
if (expectedToBeValid) {
throw new AssertionError("false failure for " + inputDescription, e);
}
+ verifyStacktraces(e);
}
}
+ private static void verifyStacktraces(ValidationException e) {
+ assertNotEquals(0, e.getStackTrace().length);
+ assertEmptyCauseStackTraces(e).ifPresent(nonempty -> {
+ throw new AssertionError("non-empty stacktrace: " + nonempty);
+ });
+ }
+
+ private static Optional assertEmptyCauseStackTraces(ValidationException e) {
+ return e.getCausingExceptions().stream().filter(exc -> exc.getStackTrace().length > 0)
+ .findFirst()
+ .map(Optional::of)
+ .orElseGet(() -> e.getCausingExceptions().stream()
+ .map(TestCase::assertEmptyCauseStackTraces)
+ .filter(Optional::isPresent)
+ .findFirst()
+ .orElse(Optional.empty()));
+ }
+
public void loadSchema(SchemaLoader.SchemaLoaderBuilder loaderBuilder) {
try {
SchemaLoader loader = loaderBuilder.schemaJson(schemaJson).build();