Skip to content

Commit 262d3ee

Browse files
fduttonFaron Dutton
andauthored
Improves performance (#731)
* Removes need for network access when executing unit-tests Resolves #729 * Replaces the use of HashSet due to poor performance * Precomputes keyword versions * Reduces the number of redundant calls to CollectorContext.getInstance() * Delays detecting the schema version until actually needed. --------- Co-authored-by: Faron Dutton <[email protected]>
1 parent 86c6ac6 commit 262d3ee

18 files changed

+115
-212
lines changed

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

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -64,12 +64,9 @@ public AdditionalPropertiesValidator(String schemaPath, JsonNode schemaNode, Jso
6464
parseErrorCode(getValidatorType().getErrorCodeKey());
6565
}
6666

67-
private void addToEvaluatedProperties(String propertyPath) {
68-
CollectorContext.getInstance().getEvaluatedProperties().add(propertyPath);
69-
}
70-
7167
public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String at) {
7268
debug(logger, node, rootNode, at);
69+
CollectorContext collectorContext = CollectorContext.getInstance();
7370

7471
Set<ValidationMessage> errors = new LinkedHashSet<ValidationMessage>();
7572
if (!node.isObject()) {
@@ -80,7 +77,7 @@ public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String
8077
// if allowAdditionalProperties is true, add all the properties as evaluated.
8178
if (allowAdditionalProperties) {
8279
for (Iterator<String> it = node.fieldNames(); it.hasNext(); ) {
83-
addToEvaluatedProperties(atPath(at, it.next()));
80+
collectorContext.getEvaluatedProperties().add(atPath(at, it.next()));
8481
}
8582
}
8683

@@ -104,7 +101,7 @@ public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String
104101
errors.add(buildValidationMessage(at, pname));
105102
} else {
106103
if (additionalPropertiesSchema != null) {
107-
ValidatorState state = (ValidatorState) CollectorContext.getInstance().get(ValidatorState.VALIDATOR_STATE_KEY);
104+
ValidatorState state = (ValidatorState) collectorContext.get(ValidatorState.VALIDATOR_STATE_KEY);
108105
if (state != null && state.isWalkEnabled()) {
109106
errors.addAll(additionalPropertiesSchema.walk(node.get(pname), rootNode, atPath(at, pname), state.isValidationEnabled()));
110107
} else {

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

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -43,23 +43,24 @@ public AllOfValidator(String schemaPath, JsonNode schemaNode, JsonSchema parentS
4343

4444
public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String at) {
4545
debug(logger, node, rootNode, at);
46+
CollectorContext collectorContext = CollectorContext.getInstance();
4647

4748
// get the Validator state object storing validation data
48-
ValidatorState state = (ValidatorState) CollectorContext.getInstance().get(ValidatorState.VALIDATOR_STATE_KEY);
49+
ValidatorState state = (ValidatorState) collectorContext.get(ValidatorState.VALIDATOR_STATE_KEY);
4950

5051
Set<ValidationMessage> childSchemaErrors = new LinkedHashSet<ValidationMessage>();
5152

52-
Set<String> newEvaluatedProperties = Collections.emptySet();
53+
Collection<String> newEvaluatedProperties = Collections.emptyList();
5354

5455
for (JsonSchema schema : schemas) {
5556
// As AllOf might contain multiple schemas take a backup of evaluatedProperties.
56-
Set<String> backupEvaluatedProperties = CollectorContext.getInstance().copyEvaluatedProperties();
57+
Collection<String> backupEvaluatedProperties = collectorContext.getEvaluatedProperties();
5758

5859
Set<ValidationMessage> localErrors = new HashSet<>();
5960

6061
try {
6162
// Make the evaluatedProperties list empty.
62-
CollectorContext.getInstance().getEvaluatedProperties().clear();
63+
collectorContext.resetEvaluatedProperties();
6364

6465
if (!state.isWalkEnabled()) {
6566
localErrors = schema.validate(node, rootNode, at);
@@ -71,7 +72,7 @@ public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String
7172

7273
// Keep Collecting total evaluated properties.
7374
if (localErrors.isEmpty()) {
74-
newEvaluatedProperties = CollectorContext.getInstance().copyEvaluatedProperties();
75+
newEvaluatedProperties = collectorContext.getEvaluatedProperties();
7576
}
7677

7778
if (this.validationContext.getConfig().isOpenAPI3StyleDiscriminators()) {
@@ -106,11 +107,11 @@ public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String
106107
}
107108
}
108109
} finally {
109-
CollectorContext.getInstance().replaceEvaluatedProperties(backupEvaluatedProperties);
110+
collectorContext.setEvaluatedProperties(backupEvaluatedProperties);
110111
if (localErrors.isEmpty()) {
111-
CollectorContext.getInstance().getEvaluatedProperties().addAll(newEvaluatedProperties);
112+
collectorContext.getEvaluatedProperties().addAll(newEvaluatedProperties);
112113
}
113-
newEvaluatedProperties = Collections.emptySet();
114+
newEvaluatedProperties = Collections.emptyList();
114115
}
115116
}
116117

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

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,10 @@ public AnyOfValidator(String schemaPath, JsonNode schemaNode, JsonSchema parentS
5151

5252
public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String at) {
5353
debug(logger, node, rootNode, at);
54+
CollectorContext collectorContext = CollectorContext.getInstance();
5455

5556
// get the Validator state object storing validation data
56-
ValidatorState state = (ValidatorState) CollectorContext.getInstance().get(ValidatorState.VALIDATOR_STATE_KEY);
57+
ValidatorState state = (ValidatorState) collectorContext.get(ValidatorState.VALIDATOR_STATE_KEY);
5758

5859
if (this.validationContext.getConfig().isOpenAPI3StyleDiscriminators()) {
5960
validationContext.enterDiscriminatorContext(this.discriminatorContext, at);
@@ -64,10 +65,10 @@ public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String
6465
Set<ValidationMessage> allErrors = new LinkedHashSet<>();
6566

6667
// As anyOf might contain multiple schemas take a backup of evaluatedProperties.
67-
Set<String> backupEvaluatedProperties = CollectorContext.getInstance().copyEvaluatedProperties();
68+
Collection<String> backupEvaluatedProperties = collectorContext.getEvaluatedProperties();
6869

6970
// Make the evaluatedProperties list empty.
70-
CollectorContext.getInstance().getEvaluatedProperties().clear();
71+
collectorContext.resetEvaluatedProperties();
7172

7273
try {
7374
int numberOfValidSubSchemas = 0;
@@ -141,9 +142,9 @@ public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String
141142
if (allErrors.isEmpty()) {
142143
state.setMatchedNode(true);
143144
} else {
144-
CollectorContext.getInstance().getEvaluatedProperties().clear();
145+
collectorContext.getEvaluatedProperties().clear();
145146
}
146-
CollectorContext.getInstance().getEvaluatedProperties().addAll(backupEvaluatedProperties);
147+
collectorContext.getEvaluatedProperties().addAll(backupEvaluatedProperties);
147148
}
148149
return Collections.unmodifiableSet(allErrors);
149150
}

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

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@
1515
*/
1616
package com.networknt.schema;
1717

18+
import java.util.ArrayList;
1819
import java.util.Collection;
1920
import java.util.HashMap;
20-
import java.util.LinkedHashSet;
2121
import java.util.Map;
2222
import java.util.Map.Entry;
2323
import java.util.Set;
@@ -50,35 +50,30 @@ public static CollectorContext getInstance() {
5050
/**
5151
* Used to track which properties have been evaluated.
5252
*/
53-
private final Set<String> evaluatedProperties = new LinkedHashSet<>();
53+
private Collection<String> evaluatedProperties = new ArrayList<>();
5454

5555
/**
5656
* Identifies which properties have been evaluated.
5757
*
5858
* @return the set of evaluated properties (never null)
5959
*/
60-
public Set<String> getEvaluatedProperties() {
60+
public Collection<String> getEvaluatedProperties() {
6161
return evaluatedProperties;
6262
}
6363

6464
/**
65-
* Clones the properties that have been evaluated.
66-
*
67-
* @return the set of evaluated properties (never null)
65+
* Set the properties that have been evaluated.
66+
* @param paths the set of evaluated properties (may be null)
6867
*/
69-
public Set<String> copyEvaluatedProperties() {
70-
return new LinkedHashSet<>(evaluatedProperties);
68+
public void setEvaluatedProperties(Collection<String> paths) {
69+
this.evaluatedProperties = null != paths ? paths : new ArrayList<>();
7170
}
7271

7372
/**
74-
* Replaces the properties that have been evaluated.
75-
* @param paths the set of evaluated properties (may be null)
73+
* Replaces the properties that have been evaluated with an empty collection.
7674
*/
77-
public void replaceEvaluatedProperties(Collection<String> paths) {
78-
this.evaluatedProperties.clear();
79-
if (null != paths) {
80-
this.evaluatedProperties.addAll(paths);
81-
}
75+
public void resetEvaluatedProperties() {
76+
this.evaluatedProperties = new ArrayList<>();
8277
}
8378

8479
/**

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

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -57,18 +57,19 @@ public IfValidator(String schemaPath, JsonNode schemaNode, JsonSchema parentSche
5757

5858
public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String at) {
5959
debug(logger, node, rootNode, at);
60+
CollectorContext collectorContext = CollectorContext.getInstance();
6061

6162
// As if-then-else might contain multiple schemas take a backup of evaluatedProperties.
62-
Set<String> backupEvaluatedProperties = CollectorContext.getInstance().copyEvaluatedProperties();
63+
Collection<String> backupEvaluatedProperties = collectorContext.getEvaluatedProperties();
6364

64-
Set<String> ifEvaluatedProperties = Collections.emptySet();
65+
Collection<String> ifEvaluatedProperties = Collections.emptyList();
6566

66-
Set<String> thenEvaluatedProperties = Collections.emptySet();
67+
Collection<String> thenEvaluatedProperties = Collections.emptyList();
6768

68-
Set<String> elseEvaluatedProperties = Collections.emptySet();
69+
Collection<String> elseEvaluatedProperties = Collections.emptyList();
6970

7071
// Make the evaluatedProperties list empty.
71-
CollectorContext.getInstance().getEvaluatedProperties().clear();
72+
collectorContext.resetEvaluatedProperties();
7273

7374
Set<ValidationMessage> errors = new LinkedHashSet<ValidationMessage>();
7475

@@ -82,38 +83,38 @@ public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String
8283
ifConditionPassed = false;
8384
}
8485
// Evaluated Properties from if.
85-
ifEvaluatedProperties = CollectorContext.getInstance().copyEvaluatedProperties();
86+
ifEvaluatedProperties = collectorContext.getEvaluatedProperties();
8687

8788
if (ifConditionPassed && thenSchema != null) {
8889

8990
// Make the evaluatedProperties list empty.
90-
CollectorContext.getInstance().getEvaluatedProperties().clear();
91+
collectorContext.resetEvaluatedProperties();
9192

9293
errors.addAll(thenSchema.validate(node, rootNode, at));
9394

9495
// Collect the then evaluated properties.
95-
thenEvaluatedProperties = CollectorContext.getInstance().copyEvaluatedProperties();
96+
thenEvaluatedProperties = collectorContext.getEvaluatedProperties();
9697

9798
} else if (!ifConditionPassed && elseSchema != null) {
9899

99100
// Make the evaluatedProperties list empty.
100-
CollectorContext.getInstance().getEvaluatedProperties().clear();
101+
collectorContext.resetEvaluatedProperties();
101102

102103
errors.addAll(elseSchema.validate(node, rootNode, at));
103104

104105
// Collect the else evaluated properties.
105-
elseEvaluatedProperties = CollectorContext.getInstance().copyEvaluatedProperties();
106+
elseEvaluatedProperties = collectorContext.getEvaluatedProperties();
106107
}
107108

108109
} finally {
109-
CollectorContext.getInstance().replaceEvaluatedProperties(backupEvaluatedProperties);
110+
collectorContext.setEvaluatedProperties(backupEvaluatedProperties);
110111
if (errors.isEmpty()) {
111112
// If the "if" keyword condition is passed then only add if properties as evaluated.
112113
if (ifConditionPassed) {
113-
CollectorContext.getInstance().getEvaluatedProperties().addAll(ifEvaluatedProperties);
114+
collectorContext.getEvaluatedProperties().addAll(ifEvaluatedProperties);
114115
}
115-
CollectorContext.getInstance().getEvaluatedProperties().addAll(thenEvaluatedProperties);
116-
CollectorContext.getInstance().getEvaluatedProperties().addAll(elseEvaluatedProperties);
116+
collectorContext.getEvaluatedProperties().addAll(thenEvaluatedProperties);
117+
collectorContext.getEvaluatedProperties().addAll(elseEvaluatedProperties);
117118
}
118119
}
119120

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -467,14 +467,16 @@ public Set<ValidationMessage> walk(JsonNode node, JsonNode rootNode, String at,
467467
/************************ END OF WALK METHODS **********************************/
468468

469469
private void setValidatorState(boolean isWalkEnabled, boolean shouldValidateSchema) {
470+
CollectorContext collectorContext = CollectorContext.getInstance();
471+
470472
// Get the Validator state object storing validation data
471-
Object stateObj = CollectorContext.getInstance().get(ValidatorState.VALIDATOR_STATE_KEY);
473+
Object stateObj = collectorContext.get(ValidatorState.VALIDATOR_STATE_KEY);
472474
// if one has not been created, instantiate one
473475
if (stateObj == null) {
474476
ValidatorState state = new ValidatorState();
475477
state.setWalkEnabled(isWalkEnabled);
476478
state.setValidationEnabled(shouldValidateSchema);
477-
CollectorContext.getInstance().add(ValidatorState.VALIDATOR_STATE_KEY, state);
479+
collectorContext.add(ValidatorState.VALIDATOR_STATE_KEY, state);
478480
}
479481
}
480482

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

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,14 @@ public NotValidator(String schemaPath, JsonNode schemaNode, JsonSchema parentSch
3535
}
3636

3737
public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String at) {
38+
CollectorContext collectorContext = CollectorContext.getInstance();
3839
Set<ValidationMessage> errors = new HashSet<>();
3940

4041
//As not will contain a schema take a backup of evaluatedProperties.
41-
Set<String> backupEvaluatedProperties = CollectorContext.getInstance().copyEvaluatedProperties();
42+
Collection<String> backupEvaluatedProperties = collectorContext.getEvaluatedProperties();
4243

4344
// Make the evaluatedProperties list empty.
44-
CollectorContext.getInstance().getEvaluatedProperties().clear();
45+
collectorContext.resetEvaluatedProperties();
4546

4647
try {
4748
debug(logger, node, rootNode, at);
@@ -52,9 +53,9 @@ public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String
5253
return Collections.emptySet();
5354
} finally {
5455
if (errors.isEmpty()) {
55-
CollectorContext.getInstance().getEvaluatedProperties().addAll(backupEvaluatedProperties);
56+
collectorContext.getEvaluatedProperties().addAll(backupEvaluatedProperties);
5657
} else {
57-
CollectorContext.getInstance().replaceEvaluatedProperties(backupEvaluatedProperties);
58+
collectorContext.setEvaluatedProperties(backupEvaluatedProperties);
5859
}
5960
}
6061
}

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

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,17 +39,18 @@ public OneOfValidator(String schemaPath, JsonNode schemaNode, JsonSchema parentS
3939

4040
public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String at) {
4141
Set<ValidationMessage> errors = new LinkedHashSet<>();
42+
CollectorContext collectorContext = CollectorContext.getInstance();
4243

4344
// As oneOf might contain multiple schemas take a backup of evaluatedProperties.
44-
Set<String> backupEvaluatedProperties = CollectorContext.getInstance().copyEvaluatedProperties();
45+
Collection<String> backupEvaluatedProperties = collectorContext.getEvaluatedProperties();
4546

4647
// Make the evaluatedProperties list empty.
47-
CollectorContext.getInstance().getEvaluatedProperties().clear();
48+
collectorContext.resetEvaluatedProperties();
4849

4950
try {
5051
debug(logger, node, rootNode, at);
5152

52-
ValidatorState state = (ValidatorState) CollectorContext.getInstance().get(ValidatorState.VALIDATOR_STATE_KEY);
53+
ValidatorState state = (ValidatorState) collectorContext.get(ValidatorState.VALIDATOR_STATE_KEY);
5354

5455
// this is a complex validator, we set the flag to true
5556
state.setComplexValidator(true);
@@ -79,7 +80,7 @@ public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String
7980

8081
// If the number of valid schema is greater than one, just reset the evaluated properties and break.
8182
if (numberOfValidSchema > 1) {
82-
CollectorContext.getInstance().getEvaluatedProperties().clear();
83+
collectorContext.resetEvaluatedProperties();
8384
break;
8485
}
8586

@@ -106,9 +107,9 @@ public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String
106107
return Collections.unmodifiableSet(errors);
107108
} finally {
108109
if (errors.isEmpty()) {
109-
CollectorContext.getInstance().getEvaluatedProperties().addAll(backupEvaluatedProperties);
110+
collectorContext.getEvaluatedProperties().addAll(backupEvaluatedProperties);
110111
} else {
111-
CollectorContext.getInstance().replaceEvaluatedProperties(backupEvaluatedProperties);
112+
collectorContext.setEvaluatedProperties(backupEvaluatedProperties);
112113
}
113114
}
114115
}

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

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String
5858
for (Map.Entry<Pattern, JsonSchema> entry : schemas.entrySet()) {
5959
Matcher m = entry.getKey().matcher(name);
6060
if (m.find()) {
61-
addToEvaluatedProperties(atPath(at, name));
61+
CollectorContext.getInstance().getEvaluatedProperties().add(atPath(at, name));
6262
errors.addAll(entry.getValue().validate(n, rootNode, atPath(at, name)));
6363
}
6464
}
@@ -70,8 +70,4 @@ public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String
7070
public void preloadJsonSchema() {
7171
preloadJsonSchemas(schemas.values());
7272
}
73-
74-
private void addToEvaluatedProperties(String propertyPath) {
75-
CollectorContext.getInstance().getEvaluatedProperties().add(propertyPath);
76-
}
7773
}

0 commit comments

Comments
 (0)