Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,9 @@ public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String
errors.add(buildValidationMessage(at, pname));
} else {
if (additionalPropertiesSchema != null) {
ValidatorState state = validatorState.get();
ValidatorState state = (ValidatorState) CollectorContext.getInstance().get(ValidatorState.VALIDATOR_STATE_KEY);
if (state != null && state.isWalkEnabled()) {
errors.addAll(additionalPropertiesSchema.walk(node.get(pname), rootNode, at + "." + pname, state.isValidationEnabledWhileWalking()));
errors.addAll(additionalPropertiesSchema.walk(node.get(pname), rootNode, at + "." + pname, state.isValidationEnabled()));
} else {
errors.addAll(additionalPropertiesSchema.validate(node.get(pname), rootNode, at + "." + pname));
}
Expand Down
5 changes: 0 additions & 5 deletions src/main/java/com/networknt/schema/BaseJsonValidator.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,6 @@ public abstract class BaseJsonValidator implements JsonValidator {
protected SchemaValidatorsConfig config;
protected final boolean failFast;

/**
* ThreadLocal to allow to pass state in recursive validator calls
*/
protected final static ThreadLocal<ValidatorState> validatorState = new ThreadLocal<ValidatorState>();

public BaseJsonValidator(String schemaPath, JsonNode schemaNode, JsonSchema parentSchema,
ValidatorTypeCode validatorType, ValidationContext validationContext) {
this(schemaPath, schemaNode, parentSchema, validatorType, false,
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/com/networknt/schema/CollectorContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ public void combineWithCollector(String name, Object data) {
/**
* Reset the context
*/
void reset() {
public void reset() {
this.collectorMap = new HashMap<String, Object>();
this.collectorLoadMap = new HashMap<String, Object>();
}
Expand Down
120 changes: 67 additions & 53 deletions src/main/java/com/networknt/schema/JsonSchema.java
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,12 @@ private Map<String, JsonValidator> read(JsonNode schemaNode) {

public Set<ValidationMessage> validate(JsonNode jsonNode, JsonNode rootNode, String at) {
Set<ValidationMessage> errors = new LinkedHashSet<ValidationMessage>();
// Get the collector context.
getCollectorContext();
// Set the walkEnabled and isValidationEnabled flag in internal validator state.
setValidatorState(false, true);
for (JsonValidator v : getValidators().values()) {
// Validate.
errors.addAll(v.validate(jsonNode, rootNode, at));
}
return errors;
Expand All @@ -230,25 +235,21 @@ public ValidationResult validateAndCollect(JsonNode node) {
return validateAndCollect(node, node, AT_ROOT);
}


/**
* This method both validates and collects the data in a CollectionContext.
* This method both validates and collects the data in a CollectorContext.
* Unlike others this methods cleans and removes everything from collector
* context before returning.
*
* @param jsonNode JsonNode
* @param rootNode JsonNode
* @param at String path
* @param at String path
* @return ValidationResult
*/
protected ValidationResult validateAndCollect(JsonNode jsonNode, JsonNode rootNode, String at) {
try {
CollectorContext collectorContext;
if(this.config !=null && this.config.getCollectorContext() != null){
collectorContext = this.config.getCollectorContext();
} else {
collectorContext = new CollectorContext();
}
// Set the collector context in thread info, this is unique for every thread.
ThreadInfo.set(CollectorContext.COLLECTOR_CONTEXT_THREAD_LOCAL_KEY, collectorContext);
// Get the collector context from the thread local.
CollectorContext collectorContext = getCollectorContext();
// Valdiate.
Set<ValidationMessage> errors = validate(jsonNode, rootNode, at);
// Load all the data from collectors into the context.
collectorContext.loadCollectors();
Expand All @@ -271,58 +272,71 @@ protected ValidationResult validateAndCollect(JsonNode jsonNode, JsonNode rootNo
* @return result of ValidationResult
*/
public ValidationResult walk(JsonNode node, boolean shouldValidateSchema) {
// Create the collector context object.
CollectorContext collectorContext = new CollectorContext();
// Set the collector context in thread info, this is unique for every thread.
ThreadInfo.set(CollectorContext.COLLECTOR_CONTEXT_THREAD_LOCAL_KEY, collectorContext);
// Get the collector context from the thread local.
CollectorContext collectorContext = getCollectorContext();
// Set the walkEnabled flag in internal validator state.
setValidatorState(true, shouldValidateSchema);
// Walk through the schema.
Set<ValidationMessage> errors = walk(node, node, AT_ROOT, shouldValidateSchema);
// Load all the data from collectors into the context.
collectorContext.loadCollectors();
// Collect errors and collector context into validation result.
ValidationResult validationResult = new ValidationResult(errors, collectorContext);
return validationResult;
}
Set<ValidationMessage> errors = walk(node, node, AT_ROOT, shouldValidateSchema);
// Load all the data from collectors into the context.
collectorContext.loadCollectors();
// Collect errors and collector context into validation result.
ValidationResult validationResult = new ValidationResult(errors, collectorContext);
return validationResult;
}


@Override
public Set<ValidationMessage> walk(JsonNode node, JsonNode rootNode, String at, boolean shouldValidateSchema) {
Set<ValidationMessage> validationMessages = new LinkedHashSet<ValidationMessage>();
// Walk through all the JSONWalker's.
for (Entry<String, JsonValidator> entry : getValidators().entrySet()) {
JsonSchemaWalker jsonWalker = entry.getValue();
String schemaPathWithKeyword = entry.getKey();
try {
// Call all the pre-walk listeners. If all the pre-walk listeners return true
// then continue to walk method.
if (keywordWalkListenerRunner.runPreWalkListeners(schemaPathWithKeyword, node, rootNode, at, schemaPath,
schemaNode, parentSchema, validationContext.getJsonSchemaFactory())) {
validationMessages.addAll(jsonWalker.walk(node, rootNode, at, shouldValidateSchema));
}
} finally {
// Call all the post-walk listeners.
keywordWalkListenerRunner.runPostWalkListeners(schemaPathWithKeyword, node, rootNode, at, schemaPath,
schemaNode, parentSchema, validationContext.getJsonSchemaFactory(), validationMessages);
}
}
return validationMessages;
public Set<ValidationMessage> walk(JsonNode node, JsonNode rootNode, String at, boolean shouldValidateSchema) {
Set<ValidationMessage> validationMessages = new LinkedHashSet<ValidationMessage>();
// Walk through all the JSONWalker's.
for (Entry<String, JsonValidator> entry : getValidators().entrySet()) {
JsonSchemaWalker jsonWalker = entry.getValue();
String schemaPathWithKeyword = entry.getKey();
try {
// Call all the pre-walk listeners. If atleast one of the pre walk listeners
// returns SKIP, then skip the walk.
if (keywordWalkListenerRunner.runPreWalkListeners(schemaPathWithKeyword, node, rootNode, at, schemaPath,
schemaNode, parentSchema, validationContext.getJsonSchemaFactory())) {
validationMessages.addAll(jsonWalker.walk(node, rootNode, at, shouldValidateSchema));
}
} finally {
// Call all the post-walk listeners.
keywordWalkListenerRunner.runPostWalkListeners(schemaPathWithKeyword, node, rootNode, at, schemaPath,
schemaNode, parentSchema, validationContext.getJsonSchemaFactory(), validationMessages);
}
}
return validationMessages;
}

/************************ END OF WALK METHODS **********************************/

private void setValidatorState(boolean isWalkEnabled, boolean shouldValidateSchema) {
// Get the Validator state object storing validation data
ValidatorState state = validatorState.get();
if (state == null) {
// if one has not been created, instantiate one
state = new ValidatorState();
state.setWalkEnabled(isWalkEnabled);
state.setValidationEnabledWhileWalking(shouldValidateSchema);
validatorState.set(state);
}
}
private void setValidatorState(boolean isWalkEnabled, boolean shouldValidateSchema) {
// Get the Validator state object storing validation data
Object stateObj = CollectorContext.getInstance().get(ValidatorState.VALIDATOR_STATE_KEY);
// if one has not been created, instantiate one
if (stateObj == null) {
ValidatorState state = new ValidatorState();
state.setWalkEnabled(isWalkEnabled);
state.setValidationEnabled(shouldValidateSchema);
CollectorContext.getInstance().add(ValidatorState.VALIDATOR_STATE_KEY, state);
}
}


public CollectorContext getCollectorContext() {
CollectorContext collectorContext = (CollectorContext) ThreadInfo.get(CollectorContext.COLLECTOR_CONTEXT_THREAD_LOCAL_KEY);
if (collectorContext == null) {
if (this.config != null && this.config.getCollectorContext() != null) {
collectorContext = this.config.getCollectorContext();
} else {
collectorContext = new CollectorContext();
}
// Set the collector context in thread info, this is unique for every thread.
ThreadInfo.set(CollectorContext.COLLECTOR_CONTEXT_THREAD_LOCAL_KEY, collectorContext);
}
return collectorContext;
}

@Override
public String toString() {
Expand Down
18 changes: 10 additions & 8 deletions src/main/java/com/networknt/schema/OneOfValidator.java
Original file line number Diff line number Diff line change
Expand Up @@ -128,15 +128,10 @@ public OneOfValidator(String schemaPath, JsonNode schemaNode, JsonSchema parentS
public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String at) {
debug(logger, node, rootNode, at);

ValidatorState state = validatorState.get();
if (state == null) {
state = new ValidatorState();
validatorState.set(state);
}
ValidatorState state = (ValidatorState) CollectorContext.getInstance().get(ValidatorState.VALIDATOR_STATE_KEY);
// this is a complex validator, we set the flag to true
state.setComplexValidator(true);


int numberOfValidSchema = 0;
Set<ValidationMessage> errors = new LinkedHashSet<ValidationMessage>();
Set<ValidationMessage> childErrors = new LinkedHashSet<ValidationMessage>();
Expand Down Expand Up @@ -164,7 +159,7 @@ public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String
if (!state.isWalkEnabled()) {
schemaErrors = schema.validate(node, rootNode, at);
} else {
schemaErrors = schema.walk(node, rootNode, at, state.isValidationEnabledWhileWalking());
schemaErrors = schema.walk(node, rootNode, at, state.isValidationEnabled());
}

// check if any validation errors have occurred
Expand Down Expand Up @@ -202,11 +197,17 @@ else if (numberOfValidSchema < 1) {
state.setMatchedNode(true);

// reset the ValidatorState object in the ThreadLocal
validatorState.remove();
resetValidatorState();

return Collections.unmodifiableSet(errors);
}

private void resetValidatorState() {
ValidatorState state = (ValidatorState) CollectorContext.getInstance().get(ValidatorState.VALIDATOR_STATE_KEY);
state.setComplexValidator(false);
state.setMatchedNode(true);
}

public List<JsonSchema> getChildSchemas() {
List<JsonSchema> childJsonSchemas = new ArrayList<JsonSchema>();
for (ShortcutValidator shortcutValidator: schemas ) {
Expand Down Expand Up @@ -241,4 +242,5 @@ private ValidationMessage getMultiSchemasValidErrorMsg(String at){
return message;
}


}
9 changes: 2 additions & 7 deletions src/main/java/com/networknt/schema/PropertiesValidator.java
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,7 @@ public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String
Set<ValidationMessage> errors = new LinkedHashSet<ValidationMessage>();

// get the Validator state object storing validation data
ValidatorState state = validatorState.get();
if (state == null) {
// if one has not been created, instantiate one
state = new ValidatorState();
validatorState.set(state);
}
ValidatorState state = (ValidatorState) CollectorContext.getInstance().get(ValidatorState.VALIDATOR_STATE_KEY);

for (Map.Entry<String, JsonSchema> entry : schemas.entrySet()) {
JsonSchema propertySchema = entry.getValue();
Expand All @@ -75,7 +70,7 @@ public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String
errors.addAll(propertySchema.validate(propertyNode, rootNode, at + "." + entry.getKey()));
} else {
// check if walker is enabled. If it is enabled it is upto the walker implementation to decide about the validation.
walkSchema(entry, node, rootNode, at, state.isValidationEnabledWhileWalking(), errors);
walkSchema(entry, node, rootNode, at, state.isValidationEnabled(), errors);
}

// reset the complex flag to the original value before the recursive call
Expand Down
13 changes: 8 additions & 5 deletions src/main/java/com/networknt/schema/ValidatorState.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
package com.networknt.schema;

public class ValidatorState {

static final String VALIDATOR_STATE_KEY = "com.networknt.schema.ValidatorState";

/**
* Flag set when a node has matched Works in conjunction with the next flag:
* isComplexValidator, to be used for complex validators such as oneOf, for ex
Expand All @@ -37,7 +40,7 @@ public class ValidatorState {
/**
* Flag to check if validation is enabled while walking.
*/
private boolean isValidationEnabledWhileWalking = false;
private boolean isValidationEnabled = false;

public void setMatchedNode(boolean matchedNode) {
this.matchedNode = matchedNode;
Expand All @@ -63,12 +66,12 @@ public void setWalkEnabled(boolean isWalkEnabled) {
this.isWalkEnabled = isWalkEnabled;
}

public boolean isValidationEnabledWhileWalking() {
return isValidationEnabledWhileWalking;
public boolean isValidationEnabled() {
return isValidationEnabled;
}

public void setValidationEnabledWhileWalking(boolean isValidationEnabledWhileWalking) {
this.isValidationEnabledWhileWalking = isValidationEnabledWhileWalking;
public void setValidationEnabled(boolean isValidationEnabled) {
this.isValidationEnabled = isValidationEnabled;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -23,20 +23,23 @@ protected WalkEvent constructWalkEvent(String keyWordName, JsonNode node, JsonNo
}

protected boolean runPreWalkListeners(List<JsonSchemaWalkListener> walkListeners, WalkEvent walkEvent) {
boolean continueRunningListenersAndWalk = true;
boolean continueToWalkMethod = true;
if (walkListeners != null) {
for (JsonSchemaWalkListener walkListener : walkListeners) {
if (WalkFlow.SKIP.equals(walkListener.onWalkStart(walkEvent))) {
continueRunningListenersAndWalk = false;
break;
WalkFlow walkFlow = walkListener.onWalkStart(walkEvent);
if (WalkFlow.SKIP.equals(walkFlow) || WalkFlow.ABORT.equals(walkFlow)) {
continueToWalkMethod = false;
if (WalkFlow.ABORT.equals(walkFlow)) {
break;
}
}
}
}
return continueRunningListenersAndWalk;
return continueToWalkMethod;
}

protected void runPostWalkListeners(List<JsonSchemaWalkListener> walkListeners, WalkEvent walkEvent,
Set<ValidationMessage> validationMessages) {
Set<ValidationMessage> validationMessages) {
if (walkListeners != null) {
for (JsonSchemaWalkListener walkListener : walkListeners) {
walkListener.onWalkEnd(walkEvent, validationMessages);
Expand Down
4 changes: 4 additions & 0 deletions src/main/java/com/networknt/schema/walk/WalkEvent.java
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ public JsonSchema getRefSchema(URI schemaUri) {
return currentJsonSchemaFactory.getSchema(schemaUri);
}

public JsonSchemaFactory getCurrentJsonSchemaFactory() {
return currentJsonSchemaFactory;
}

static class WalkEventBuilder {
private WalkEvent keywordWalkEvent = null;

Expand Down
6 changes: 4 additions & 2 deletions src/main/java/com/networknt/schema/walk/WalkFlow.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@

public enum WalkFlow {

SKIP("SkipWalk", "Skip the walk methods"),
SKIP("SkipWalk", "Skip only the walk method, but continue invoking the other listeners"),

CONTINUE("ContinueToWalk", "continue to invoke the walk method");
ABORT("Abort", "Aborts all the walk listeners and walk method itself"),

CONTINUE("ContinueToWalk", "continue to invoke the walk method and other listeners");

private String name;

Expand Down
Loading