Skip to content

Commit 4fa1ab4

Browse files
Fixing concurrency and compilation issues. (#385)
* Fixing walk issues for properties * Correcting walk changes for few validators * adding item walk listener * walk listener changes * walk listener changes * merging changes from networknt * Fixing issues with ValidatorState * changing the method name to getCollectorContext * correcting the variable names and ValidatorState logic * correcting the documentation logic * Fixing compilation issues in TypeFactoryTest * Fixing the minor brace issue Co-authored-by: Prashanth Josyula <[email protected]>
1 parent b1a8e4e commit 4fa1ab4

13 files changed

+150
-131
lines changed

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,9 +93,9 @@ public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String
9393
errors.add(buildValidationMessage(at, pname));
9494
} else {
9595
if (additionalPropertiesSchema != null) {
96-
ValidatorState state = validatorState.get();
96+
ValidatorState state = (ValidatorState) CollectorContext.getInstance().get(ValidatorState.VALIDATOR_STATE_KEY);
9797
if (state != null && state.isWalkEnabled()) {
98-
errors.addAll(additionalPropertiesSchema.walk(node.get(pname), rootNode, at + "." + pname, state.isValidationEnabledWhileWalking()));
98+
errors.addAll(additionalPropertiesSchema.walk(node.get(pname), rootNode, at + "." + pname, state.isValidationEnabled()));
9999
} else {
100100
errors.addAll(additionalPropertiesSchema.validate(node.get(pname), rootNode, at + "." + pname));
101101
}

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

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,6 @@ public abstract class BaseJsonValidator implements JsonValidator {
3737
protected SchemaValidatorsConfig config;
3838
protected final boolean failFast;
3939

40-
/**
41-
* ThreadLocal to allow to pass state in recursive validator calls
42-
*/
43-
protected final static ThreadLocal<ValidatorState> validatorState = new ThreadLocal<ValidatorState>();
44-
4540
public BaseJsonValidator(String schemaPath, JsonNode schemaNode, JsonSchema parentSchema,
4641
ValidatorTypeCode validatorType, ValidationContext validationContext) {
4742
this(schemaPath, schemaNode, parentSchema, validatorType, false,

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ public void combineWithCollector(String name, Object data) {
118118
/**
119119
* Reset the context
120120
*/
121-
void reset() {
121+
public void reset() {
122122
this.collectorMap = new HashMap<String, Object>();
123123
this.collectorLoadMap = new HashMap<String, Object>();
124124
}

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

Lines changed: 67 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,12 @@ private Map<String, JsonValidator> read(JsonNode schemaNode) {
220220

221221
public Set<ValidationMessage> validate(JsonNode jsonNode, JsonNode rootNode, String at) {
222222
Set<ValidationMessage> errors = new LinkedHashSet<ValidationMessage>();
223+
// Get the collector context.
224+
getCollectorContext();
225+
// Set the walkEnabled and isValidationEnabled flag in internal validator state.
226+
setValidatorState(false, true);
223227
for (JsonValidator v : getValidators().values()) {
228+
// Validate.
224229
errors.addAll(v.validate(jsonNode, rootNode, at));
225230
}
226231
return errors;
@@ -230,25 +235,21 @@ public ValidationResult validateAndCollect(JsonNode node) {
230235
return validateAndCollect(node, node, AT_ROOT);
231236
}
232237

233-
234238
/**
235-
* This method both validates and collects the data in a CollectionContext.
239+
* This method both validates and collects the data in a CollectorContext.
240+
* Unlike others this methods cleans and removes everything from collector
241+
* context before returning.
236242
*
237243
* @param jsonNode JsonNode
238244
* @param rootNode JsonNode
239-
* @param at String path
245+
* @param at String path
240246
* @return ValidationResult
241247
*/
242248
protected ValidationResult validateAndCollect(JsonNode jsonNode, JsonNode rootNode, String at) {
243249
try {
244-
CollectorContext collectorContext;
245-
if(this.config !=null && this.config.getCollectorContext() != null){
246-
collectorContext = this.config.getCollectorContext();
247-
} else {
248-
collectorContext = new CollectorContext();
249-
}
250-
// Set the collector context in thread info, this is unique for every thread.
251-
ThreadInfo.set(CollectorContext.COLLECTOR_CONTEXT_THREAD_LOCAL_KEY, collectorContext);
250+
// Get the collector context from the thread local.
251+
CollectorContext collectorContext = getCollectorContext();
252+
// Valdiate.
252253
Set<ValidationMessage> errors = validate(jsonNode, rootNode, at);
253254
// Load all the data from collectors into the context.
254255
collectorContext.loadCollectors();
@@ -271,58 +272,71 @@ protected ValidationResult validateAndCollect(JsonNode jsonNode, JsonNode rootNo
271272
* @return result of ValidationResult
272273
*/
273274
public ValidationResult walk(JsonNode node, boolean shouldValidateSchema) {
274-
// Create the collector context object.
275-
CollectorContext collectorContext = new CollectorContext();
276-
// Set the collector context in thread info, this is unique for every thread.
277-
ThreadInfo.set(CollectorContext.COLLECTOR_CONTEXT_THREAD_LOCAL_KEY, collectorContext);
275+
// Get the collector context from the thread local.
276+
CollectorContext collectorContext = getCollectorContext();
278277
// Set the walkEnabled flag in internal validator state.
279278
setValidatorState(true, shouldValidateSchema);
280279
// Walk through the schema.
281-
Set<ValidationMessage> errors = walk(node, node, AT_ROOT, shouldValidateSchema);
282-
// Load all the data from collectors into the context.
283-
collectorContext.loadCollectors();
284-
// Collect errors and collector context into validation result.
285-
ValidationResult validationResult = new ValidationResult(errors, collectorContext);
286-
return validationResult;
287-
}
280+
Set<ValidationMessage> errors = walk(node, node, AT_ROOT, shouldValidateSchema);
281+
// Load all the data from collectors into the context.
282+
collectorContext.loadCollectors();
283+
// Collect errors and collector context into validation result.
284+
ValidationResult validationResult = new ValidationResult(errors, collectorContext);
285+
return validationResult;
286+
}
288287

289288

290289
@Override
291-
public Set<ValidationMessage> walk(JsonNode node, JsonNode rootNode, String at, boolean shouldValidateSchema) {
292-
Set<ValidationMessage> validationMessages = new LinkedHashSet<ValidationMessage>();
293-
// Walk through all the JSONWalker's.
294-
for (Entry<String, JsonValidator> entry : getValidators().entrySet()) {
295-
JsonSchemaWalker jsonWalker = entry.getValue();
296-
String schemaPathWithKeyword = entry.getKey();
297-
try {
298-
// Call all the pre-walk listeners. If all the pre-walk listeners return true
299-
// then continue to walk method.
300-
if (keywordWalkListenerRunner.runPreWalkListeners(schemaPathWithKeyword, node, rootNode, at, schemaPath,
301-
schemaNode, parentSchema, validationContext.getJsonSchemaFactory())) {
302-
validationMessages.addAll(jsonWalker.walk(node, rootNode, at, shouldValidateSchema));
303-
}
304-
} finally {
305-
// Call all the post-walk listeners.
306-
keywordWalkListenerRunner.runPostWalkListeners(schemaPathWithKeyword, node, rootNode, at, schemaPath,
307-
schemaNode, parentSchema, validationContext.getJsonSchemaFactory(), validationMessages);
308-
}
309-
}
310-
return validationMessages;
290+
public Set<ValidationMessage> walk(JsonNode node, JsonNode rootNode, String at, boolean shouldValidateSchema) {
291+
Set<ValidationMessage> validationMessages = new LinkedHashSet<ValidationMessage>();
292+
// Walk through all the JSONWalker's.
293+
for (Entry<String, JsonValidator> entry : getValidators().entrySet()) {
294+
JsonSchemaWalker jsonWalker = entry.getValue();
295+
String schemaPathWithKeyword = entry.getKey();
296+
try {
297+
// Call all the pre-walk listeners. If atleast one of the pre walk listeners
298+
// returns SKIP, then skip the walk.
299+
if (keywordWalkListenerRunner.runPreWalkListeners(schemaPathWithKeyword, node, rootNode, at, schemaPath,
300+
schemaNode, parentSchema, validationContext.getJsonSchemaFactory())) {
301+
validationMessages.addAll(jsonWalker.walk(node, rootNode, at, shouldValidateSchema));
302+
}
303+
} finally {
304+
// Call all the post-walk listeners.
305+
keywordWalkListenerRunner.runPostWalkListeners(schemaPathWithKeyword, node, rootNode, at, schemaPath,
306+
schemaNode, parentSchema, validationContext.getJsonSchemaFactory(), validationMessages);
307+
}
308+
}
309+
return validationMessages;
311310
}
312311

313312
/************************ END OF WALK METHODS **********************************/
314313

315-
private void setValidatorState(boolean isWalkEnabled, boolean shouldValidateSchema) {
316-
// Get the Validator state object storing validation data
317-
ValidatorState state = validatorState.get();
318-
if (state == null) {
319-
// if one has not been created, instantiate one
320-
state = new ValidatorState();
321-
state.setWalkEnabled(isWalkEnabled);
322-
state.setValidationEnabledWhileWalking(shouldValidateSchema);
323-
validatorState.set(state);
324-
}
325-
}
314+
private void setValidatorState(boolean isWalkEnabled, boolean shouldValidateSchema) {
315+
// Get the Validator state object storing validation data
316+
Object stateObj = CollectorContext.getInstance().get(ValidatorState.VALIDATOR_STATE_KEY);
317+
// if one has not been created, instantiate one
318+
if (stateObj == null) {
319+
ValidatorState state = new ValidatorState();
320+
state.setWalkEnabled(isWalkEnabled);
321+
state.setValidationEnabled(shouldValidateSchema);
322+
CollectorContext.getInstance().add(ValidatorState.VALIDATOR_STATE_KEY, state);
323+
}
324+
}
325+
326+
327+
public CollectorContext getCollectorContext() {
328+
CollectorContext collectorContext = (CollectorContext) ThreadInfo.get(CollectorContext.COLLECTOR_CONTEXT_THREAD_LOCAL_KEY);
329+
if (collectorContext == null) {
330+
if (this.config != null && this.config.getCollectorContext() != null) {
331+
collectorContext = this.config.getCollectorContext();
332+
} else {
333+
collectorContext = new CollectorContext();
334+
}
335+
// Set the collector context in thread info, this is unique for every thread.
336+
ThreadInfo.set(CollectorContext.COLLECTOR_CONTEXT_THREAD_LOCAL_KEY, collectorContext);
337+
}
338+
return collectorContext;
339+
}
326340

327341
@Override
328342
public String toString() {

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

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -128,15 +128,10 @@ public OneOfValidator(String schemaPath, JsonNode schemaNode, JsonSchema parentS
128128
public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String at) {
129129
debug(logger, node, rootNode, at);
130130

131-
ValidatorState state = validatorState.get();
132-
if (state == null) {
133-
state = new ValidatorState();
134-
validatorState.set(state);
135-
}
131+
ValidatorState state = (ValidatorState) CollectorContext.getInstance().get(ValidatorState.VALIDATOR_STATE_KEY);
136132
// this is a complex validator, we set the flag to true
137133
state.setComplexValidator(true);
138134

139-
140135
int numberOfValidSchema = 0;
141136
Set<ValidationMessage> errors = new LinkedHashSet<ValidationMessage>();
142137
Set<ValidationMessage> childErrors = new LinkedHashSet<ValidationMessage>();
@@ -164,7 +159,7 @@ public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String
164159
if (!state.isWalkEnabled()) {
165160
schemaErrors = schema.validate(node, rootNode, at);
166161
} else {
167-
schemaErrors = schema.walk(node, rootNode, at, state.isValidationEnabledWhileWalking());
162+
schemaErrors = schema.walk(node, rootNode, at, state.isValidationEnabled());
168163
}
169164

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

204199
// reset the ValidatorState object in the ThreadLocal
205-
validatorState.remove();
200+
resetValidatorState();
206201

207202
return Collections.unmodifiableSet(errors);
208203
}
209204

205+
private void resetValidatorState() {
206+
ValidatorState state = (ValidatorState) CollectorContext.getInstance().get(ValidatorState.VALIDATOR_STATE_KEY);
207+
state.setComplexValidator(false);
208+
state.setMatchedNode(true);
209+
}
210+
210211
public List<JsonSchema> getChildSchemas() {
211212
List<JsonSchema> childJsonSchemas = new ArrayList<JsonSchema>();
212213
for (ShortcutValidator shortcutValidator: schemas ) {
@@ -241,4 +242,5 @@ private ValidationMessage getMultiSchemasValidErrorMsg(String at){
241242
return message;
242243
}
243244

245+
244246
}

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

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -49,12 +49,7 @@ public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String
4949
Set<ValidationMessage> errors = new LinkedHashSet<ValidationMessage>();
5050

5151
// get the Validator state object storing validation data
52-
ValidatorState state = validatorState.get();
53-
if (state == null) {
54-
// if one has not been created, instantiate one
55-
state = new ValidatorState();
56-
validatorState.set(state);
57-
}
52+
ValidatorState state = (ValidatorState) CollectorContext.getInstance().get(ValidatorState.VALIDATOR_STATE_KEY);
5853

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

8176
// reset the complex flag to the original value before the recursive call

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

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616
package com.networknt.schema;
1717

1818
public class ValidatorState {
19+
20+
static final String VALIDATOR_STATE_KEY = "com.networknt.schema.ValidatorState";
21+
1922
/**
2023
* Flag set when a node has matched Works in conjunction with the next flag:
2124
* isComplexValidator, to be used for complex validators such as oneOf, for ex
@@ -37,7 +40,7 @@ public class ValidatorState {
3740
/**
3841
* Flag to check if validation is enabled while walking.
3942
*/
40-
private boolean isValidationEnabledWhileWalking = false;
43+
private boolean isValidationEnabled = false;
4144

4245
public void setMatchedNode(boolean matchedNode) {
4346
this.matchedNode = matchedNode;
@@ -63,12 +66,12 @@ public void setWalkEnabled(boolean isWalkEnabled) {
6366
this.isWalkEnabled = isWalkEnabled;
6467
}
6568

66-
public boolean isValidationEnabledWhileWalking() {
67-
return isValidationEnabledWhileWalking;
69+
public boolean isValidationEnabled() {
70+
return isValidationEnabled;
6871
}
6972

70-
public void setValidationEnabledWhileWalking(boolean isValidationEnabledWhileWalking) {
71-
this.isValidationEnabledWhileWalking = isValidationEnabledWhileWalking;
73+
public void setValidationEnabled(boolean isValidationEnabled) {
74+
this.isValidationEnabled = isValidationEnabled;
7275
}
7376

7477
}

src/main/java/com/networknt/schema/walk/AbstractWalkListenerRunner.java

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,20 +23,23 @@ protected WalkEvent constructWalkEvent(String keyWordName, JsonNode node, JsonNo
2323
}
2424

2525
protected boolean runPreWalkListeners(List<JsonSchemaWalkListener> walkListeners, WalkEvent walkEvent) {
26-
boolean continueRunningListenersAndWalk = true;
26+
boolean continueToWalkMethod = true;
2727
if (walkListeners != null) {
2828
for (JsonSchemaWalkListener walkListener : walkListeners) {
29-
if (WalkFlow.SKIP.equals(walkListener.onWalkStart(walkEvent))) {
30-
continueRunningListenersAndWalk = false;
31-
break;
29+
WalkFlow walkFlow = walkListener.onWalkStart(walkEvent);
30+
if (WalkFlow.SKIP.equals(walkFlow) || WalkFlow.ABORT.equals(walkFlow)) {
31+
continueToWalkMethod = false;
32+
if (WalkFlow.ABORT.equals(walkFlow)) {
33+
break;
34+
}
3235
}
3336
}
3437
}
35-
return continueRunningListenersAndWalk;
38+
return continueToWalkMethod;
3639
}
3740

3841
protected void runPostWalkListeners(List<JsonSchemaWalkListener> walkListeners, WalkEvent walkEvent,
39-
Set<ValidationMessage> validationMessages) {
42+
Set<ValidationMessage> validationMessages) {
4043
if (walkListeners != null) {
4144
for (JsonSchemaWalkListener walkListener : walkListeners) {
4245
walkListener.onWalkEnd(walkEvent, validationMessages);

src/main/java/com/networknt/schema/walk/WalkEvent.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,10 @@ public JsonSchema getRefSchema(URI schemaUri) {
5454
return currentJsonSchemaFactory.getSchema(schemaUri);
5555
}
5656

57+
public JsonSchemaFactory getCurrentJsonSchemaFactory() {
58+
return currentJsonSchemaFactory;
59+
}
60+
5761
static class WalkEventBuilder {
5862
private WalkEvent keywordWalkEvent = null;
5963

src/main/java/com/networknt/schema/walk/WalkFlow.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@
22

33
public enum WalkFlow {
44

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

7-
CONTINUE("ContinueToWalk", "continue to invoke the walk method");
7+
ABORT("Abort", "Aborts all the walk listeners and walk method itself"),
8+
9+
CONTINUE("ContinueToWalk", "continue to invoke the walk method and other listeners");
810

911
private String name;
1012

0 commit comments

Comments
 (0)