Skip to content

Commit 9d78bf3

Browse files
fduttonFaron Dutton
andauthored
Ensures context is reset after validating regardless of which method is used by the client. (#812)
Resolves #810 Co-authored-by: Faron Dutton <[email protected]>
1 parent efbb37e commit 9d78bf3

File tree

2 files changed

+86
-77
lines changed

2 files changed

+86
-77
lines changed

src/main/java/com/networknt/schema/CollectorContext.java

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ public CollectorContext() {
6363
public CollectorContext(boolean disableUnevaluatedItems, boolean disableUnevaluatedProperties) {
6464
this.disableUnevaluatedItems = disableUnevaluatedItems;
6565
this.disableUnevaluatedProperties = disableUnevaluatedProperties;
66-
this.dynamicScopes.push(newScope());
66+
this.dynamicScopes.push(newTopScope());
6767
}
6868

6969
/**
@@ -187,7 +187,7 @@ public void reset() {
187187
this.collectorMap = new HashMap<>();
188188
this.collectorLoadMap = new HashMap<>();
189189
this.dynamicScopes.clear();
190-
this.dynamicScopes.push(newScope());
190+
this.dynamicScopes.push(newTopScope());
191191
}
192192

193193
/**
@@ -208,6 +208,10 @@ private Scope newScope() {
208208
return new Scope(this.disableUnevaluatedItems, this.disableUnevaluatedProperties);
209209
}
210210

211+
private Scope newTopScope() {
212+
return new Scope(true, this.disableUnevaluatedItems, this.disableUnevaluatedProperties);
213+
}
214+
211215
public static class Scope {
212216

213217
/**
@@ -220,7 +224,14 @@ public static class Scope {
220224
*/
221225
private final Collection<String> evaluatedProperties;
222226

227+
private final boolean top;
228+
223229
Scope(boolean disableUnevaluatedItems, boolean disableUnevaluatedProperties) {
230+
this(false, disableUnevaluatedItems, disableUnevaluatedProperties);
231+
}
232+
233+
Scope(boolean top, boolean disableUnevaluatedItems, boolean disableUnevaluatedProperties) {
234+
this.top = top;
224235
this.evaluatedItems = newCollection(disableUnevaluatedItems);
225236
this.evaluatedProperties = newCollection(disableUnevaluatedProperties);
226237
}
@@ -251,6 +262,10 @@ public int size() {
251262
};
252263
}
253264

265+
public boolean isTop() {
266+
return this.top;
267+
}
268+
254269
/**
255270
* Identifies which array items have been evaluated.
256271
*

src/main/java/com/networknt/schema/JsonSchema.java

Lines changed: 69 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -318,7 +318,7 @@ private long activeDialect() {
318318
};
319319

320320
private String getCustomMessage(JsonNode schemaNode, String pname) {
321-
if (this.validationContext.getConfig() != null && !this.validationContext.getConfig().isCustomMessageSupported()) {
321+
if (!this.validationContext.getConfig().isCustomMessageSupported()) {
322322
return null;
323323
}
324324
final JsonSchema parentSchema = getParentSchema();
@@ -346,18 +346,6 @@ private JsonNode getMessageNode(JsonNode schemaNode, JsonSchema parentSchema, St
346346

347347
/************************ START OF VALIDATE METHODS **********************************/
348348

349-
@Override
350-
public Set<ValidationMessage> validate(JsonNode node) {
351-
try {
352-
Set<ValidationMessage> errors = validate(node, node, atRoot());
353-
return errors;
354-
} finally {
355-
if (this.validationContext.getConfig().isResetCollectorContext()) {
356-
CollectorContext.getInstance().reset();
357-
}
358-
}
359-
}
360-
361349
@Override
362350
public Set<ValidationMessage> validate(JsonNode jsonNode, JsonNode rootNode, String at) {
363351
SchemaValidatorsConfig config = this.validationContext.getConfig();
@@ -366,56 +354,68 @@ public Set<ValidationMessage> validate(JsonNode jsonNode, JsonNode rootNode, Str
366354
CollectorContext collectorContext = getCollectorContext();
367355
// Set the walkEnabled and isValidationEnabled flag in internal validator state.
368356
setValidatorState(false, true);
369-
for (JsonValidator v : getValidators().values()) {
370-
Set<ValidationMessage> results = Collections.emptySet();
371357

372-
Scope parentScope = collectorContext.enterDynamicScope();
373-
try {
374-
results = v.validate(jsonNode, rootNode, at);
375-
} finally {
376-
Scope scope = collectorContext.exitDynamicScope();
377-
if (results.isEmpty()) {
378-
parentScope.mergeWith(scope);
379-
} else {
380-
errors.addAll(results);
381-
if (v instanceof PrefixItemsValidator || v instanceof ItemsValidator || v instanceof ItemsValidator202012 || v instanceof ContainsValidator) {
382-
collectorContext.getEvaluatedItems().addAll(scope.getEvaluatedItems());
383-
}
384-
if (v instanceof PropertiesValidator || v instanceof AdditionalPropertiesValidator || v instanceof PatternPropertiesValidator) {
385-
collectorContext.getEvaluatedProperties().addAll(scope.getEvaluatedProperties());
358+
try {
359+
for (JsonValidator v : getValidators().values()) {
360+
Set<ValidationMessage> results = Collections.emptySet();
361+
362+
Scope parentScope = collectorContext.enterDynamicScope();
363+
try {
364+
results = v.validate(jsonNode, rootNode, at);
365+
} finally {
366+
Scope scope = collectorContext.exitDynamicScope();
367+
if (results.isEmpty()) {
368+
parentScope.mergeWith(scope);
369+
} else {
370+
errors.addAll(results);
371+
if (v instanceof PrefixItemsValidator || v instanceof ItemsValidator
372+
|| v instanceof ItemsValidator202012 || v instanceof ContainsValidator) {
373+
collectorContext.getEvaluatedItems().addAll(scope.getEvaluatedItems());
374+
}
375+
if (v instanceof PropertiesValidator || v instanceof AdditionalPropertiesValidator
376+
|| v instanceof PatternPropertiesValidator) {
377+
collectorContext.getEvaluatedProperties().addAll(scope.getEvaluatedProperties());
378+
}
386379
}
380+
387381
}
388382
}
389-
}
390383

391-
if (null != config && config.isOpenAPI3StyleDiscriminators()) {
392-
ObjectNode discriminator = (ObjectNode) this.schemaNode.get("discriminator");
393-
if (null != discriminator) {
394-
final DiscriminatorContext discriminatorContext = this.validationContext.getCurrentDiscriminatorContext();
395-
if (null != discriminatorContext) {
396-
final ObjectNode discriminatorToUse;
397-
final ObjectNode discriminatorFromContext = discriminatorContext.getDiscriminatorForPath(this.schemaPath);
398-
if (null == discriminatorFromContext) {
399-
// register the current discriminator. This can only happen when the current context discriminator
400-
// was not registered via allOf. In that case we have a $ref to the schema with discriminator that gets
401-
// used for validation before allOf validation has kicked in
402-
discriminatorContext.registerDiscriminator(this.schemaPath, discriminator);
403-
discriminatorToUse = discriminator;
404-
} else {
405-
discriminatorToUse = discriminatorFromContext;
384+
if (config.isOpenAPI3StyleDiscriminators()) {
385+
ObjectNode discriminator = (ObjectNode) this.schemaNode.get("discriminator");
386+
if (null != discriminator) {
387+
final DiscriminatorContext discriminatorContext = this.validationContext
388+
.getCurrentDiscriminatorContext();
389+
if (null != discriminatorContext) {
390+
final ObjectNode discriminatorToUse;
391+
final ObjectNode discriminatorFromContext = discriminatorContext
392+
.getDiscriminatorForPath(this.schemaPath);
393+
if (null == discriminatorFromContext) {
394+
// register the current discriminator. This can only happen when the current context discriminator
395+
// was not registered via allOf. In that case we have a $ref to the schema with discriminator that gets
396+
// used for validation before allOf validation has kicked in
397+
discriminatorContext.registerDiscriminator(this.schemaPath, discriminator);
398+
discriminatorToUse = discriminator;
399+
} else {
400+
discriminatorToUse = discriminatorFromContext;
401+
}
402+
403+
final String discriminatorPropertyName = discriminatorToUse.get("propertyName").asText();
404+
final JsonNode discriminatorNode = jsonNode.get(discriminatorPropertyName);
405+
final String discriminatorPropertyValue = discriminatorNode == null ? null
406+
: discriminatorNode.asText();
407+
checkDiscriminatorMatch(discriminatorContext, discriminatorToUse, discriminatorPropertyValue,
408+
this);
406409
}
407-
408-
final String discriminatorPropertyName = discriminatorToUse.get("propertyName").asText();
409-
final JsonNode discriminatorNode = jsonNode.get(discriminatorPropertyName);
410-
final String discriminatorPropertyValue = discriminatorNode == null ? null : discriminatorNode.asText();
411-
checkDiscriminatorMatch(discriminatorContext,
412-
discriminatorToUse,
413-
discriminatorPropertyValue,
414-
this);
415410
}
416411
}
412+
413+
return errors;
414+
} finally {
415+
if (collectorContext.getDynamicScope().isTop() && config.isResetCollectorContext()) {
416+
collectorContext.reset();
417+
}
417418
}
418-
return errors;
419419
}
420420

421421
public ValidationResult validateAndCollect(JsonNode node) {
@@ -433,28 +433,22 @@ public ValidationResult validateAndCollect(JsonNode node) {
433433
* @return ValidationResult
434434
*/
435435
private ValidationResult validateAndCollect(JsonNode jsonNode, JsonNode rootNode, String at) {
436-
try {
437-
// Get the config.
438-
SchemaValidatorsConfig config = this.validationContext.getConfig();
439-
// Get the collector context from the thread local.
440-
CollectorContext collectorContext = getCollectorContext();
441-
// Set the walkEnabled and isValidationEnabled flag in internal validator state.
442-
setValidatorState(false, true);
443-
// Validate.
444-
Set<ValidationMessage> errors = validate(jsonNode, rootNode, at);
445-
// When walk is called in series of nested call we don't want to load the collectors every time. Leave to the API to decide when to call collectors.
446-
if (config.doLoadCollectors()) {
447-
// Load all the data from collectors into the context.
448-
collectorContext.loadCollectors();
449-
}
450-
// Collect errors and collector context into validation result.
451-
ValidationResult validationResult = new ValidationResult(errors, collectorContext);
452-
return validationResult;
453-
} finally {
454-
if (this.validationContext.getConfig().isResetCollectorContext()) {
455-
CollectorContext.getInstance().reset();
456-
}
436+
// Get the config.
437+
SchemaValidatorsConfig config = this.validationContext.getConfig();
438+
// Get the collector context from the thread local.
439+
CollectorContext collectorContext = getCollectorContext();
440+
// Set the walkEnabled and isValidationEnabled flag in internal validator state.
441+
setValidatorState(false, true);
442+
// Validate.
443+
Set<ValidationMessage> errors = validate(jsonNode, rootNode, at);
444+
// When walk is called in series of nested call we don't want to load the collectors every time. Leave to the API to decide when to call collectors.
445+
if (config.doLoadCollectors()) {
446+
// Load all the data from collectors into the context.
447+
collectorContext.loadCollectors();
457448
}
449+
// Collect errors and collector context into validation result.
450+
ValidationResult validationResult = new ValidationResult(errors, collectorContext);
451+
return validationResult;
458452
}
459453

460454
/************************ END OF VALIDATE METHODS **********************************/

0 commit comments

Comments
 (0)