Skip to content

Commit ed8d440

Browse files
FWiesnerFlorian Wiesner
and
Florian Wiesner
authored
Added JsonValidator#preloadJsonSchema() to allow eager preloading of schemas. (#410)
Co-authored-by: Florian Wiesner <Florian [email protected]>
1 parent faec7f1 commit ed8d440

24 files changed

+249
-57
lines changed

doc/quickstart.md

+4
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,10 @@ class Sample extends BaseJsonSchemaValidatorTest {
6969
JsonNode schemaNode = getJsonNodeFromStringContent(
7070
"{\"$schema\": \"http://json-schema.org/draft-06/schema#\", \"properties\": { \"id\": {\"type\": \"number\"}}}");
7171
JsonSchema schema = getJsonSchemaFromJsonNodeAutomaticVersion(schemaNode);
72+
73+
schema.initializeValidators(); // by default all schemas are loaded lazily. You can load them eagerly via
74+
// initializeValidators()
75+
7276
JsonNode node = getJsonNodeFromStringContent("{\"id\": \"2\"}");
7377
Set<ValidationMessage> errors = schema.validate(node);
7478
assertThat(errors.size(), is(1));

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

+4
Original file line numberDiff line numberDiff line change
@@ -106,4 +106,8 @@ public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String
106106
return Collections.unmodifiableSet(errors);
107107
}
108108

109+
@Override
110+
public void preloadJsonSchema() {
111+
additionalPropertiesSchema.initializeValidators();
112+
}
109113
}

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

+4
Original file line numberDiff line numberDiff line change
@@ -99,4 +99,8 @@ public Set<ValidationMessage> walk(JsonNode node, JsonNode rootNode, String at,
9999
return Collections.unmodifiableSet(validationMessages);
100100
}
101101

102+
@Override
103+
public void preloadJsonSchema() {
104+
preloadJsonSchemas(schemas);
105+
}
102106
}

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

+5
Original file line numberDiff line numberDiff line change
@@ -97,4 +97,9 @@ public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String
9797
}
9898
return Collections.unmodifiableSet(allErrors);
9999
}
100+
101+
@Override
102+
public void preloadJsonSchema() {
103+
preloadJsonSchemas(schemas);
104+
}
100105
}

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

+12-1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package com.networknt.schema;
1818

1919
import java.net.URI;
20+
import java.util.Collection;
2021
import java.util.Iterator;
2122
import java.util.LinkedHashSet;
2223
import java.util.Map;
@@ -164,6 +165,17 @@ public Set<ValidationMessage> walk(JsonNode node, JsonNode rootNode, String at,
164165
return validationMessages;
165166
}
166167

168+
@Override
169+
public void preloadJsonSchema() {
170+
// do nothing by default - to be overridden in subclasses
171+
}
172+
173+
protected void preloadJsonSchemas(final Collection<JsonSchema> schemas) {
174+
for (final JsonSchema schema: schemas) {
175+
schema.initializeValidators();
176+
}
177+
}
178+
167179
protected boolean isPartOfOneOfMultipleType() {
168180
return parentSchema.schemaPath.equals(ValidatorTypeCode.ONE_OF.getValue());
169181
}
@@ -285,5 +297,4 @@ private static boolean noExplicitDiscriminatorKeyOverride(final JsonNode discrim
285297
}
286298
return true;
287299
}
288-
289300
}

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

+9-1
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,14 @@
2626
public class ContainsValidator extends BaseJsonValidator implements JsonValidator {
2727
private static final Logger logger = LoggerFactory.getLogger(ContainsValidator.class);
2828

29-
private JsonSchema schema;
29+
private final JsonSchema schema;
3030

3131
public ContainsValidator(String schemaPath, JsonNode schemaNode, JsonSchema parentSchema, ValidationContext validationContext) {
3232
super(schemaPath, schemaNode, parentSchema, ValidatorTypeCode.CONTAINS, validationContext);
3333
if (schemaNode.isObject() || schemaNode.isBoolean()) {
3434
schema = new JsonSchema(validationContext, getValidatorType().getValue(), parentSchema.getCurrentUri(), schemaNode, parentSchema);
35+
} else {
36+
schema = null;
3537
}
3638

3739
parseErrorCode(getValidatorType().getErrorCodeKey());
@@ -70,4 +72,10 @@ private Set<ValidationMessage> buildErrorMessageSet(String at) {
7072
return Collections.singleton(buildValidationMessage(at, schema.getSchemaNode().toString()));
7173
}
7274

75+
@Override
76+
public void preloadJsonSchema() {
77+
if (null != schema) {
78+
schema.initializeValidators();
79+
}
80+
}
7381
}

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

+5-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
public class DependenciesValidator extends BaseJsonValidator implements JsonValidator {
2626
private static final Logger logger = LoggerFactory.getLogger(DependenciesValidator.class);
2727
private final Map<String, List<String>> propertyDeps = new HashMap<String, List<String>>();
28-
private Map<String, JsonSchema> schemaDeps = new HashMap<String, JsonSchema>();
28+
private final Map<String, JsonSchema> schemaDeps = new HashMap<String, JsonSchema>();
2929

3030
public DependenciesValidator(String schemaPath, JsonNode schemaNode, JsonSchema parentSchema, ValidationContext validationContext) {
3131

@@ -75,4 +75,8 @@ public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String
7575
return Collections.unmodifiableSet(errors);
7676
}
7777

78+
@Override
79+
public void preloadJsonSchema() {
80+
preloadJsonSchemas(schemaDeps.values());
81+
}
7882
}

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

+26-6
Original file line numberDiff line numberDiff line change
@@ -27,23 +27,31 @@ public class IfValidator extends BaseJsonValidator implements JsonValidator {
2727

2828
private static final ArrayList<String> KEYWORDS = new ArrayList<String>(Arrays.asList("if", "then", "else"));
2929

30-
private JsonSchema ifSchema;
31-
private JsonSchema thenSchema;
32-
private JsonSchema elseSchema;
30+
private final JsonSchema ifSchema;
31+
private final JsonSchema thenSchema;
32+
private final JsonSchema elseSchema;
3333

3434
public IfValidator(String schemaPath, JsonNode schemaNode, JsonSchema parentSchema, ValidationContext validationContext) {
3535
super(schemaPath, schemaNode, parentSchema, ValidatorTypeCode.IF_THEN_ELSE, validationContext);
3636

37+
JsonSchema foundIfSchema = null;
38+
JsonSchema foundThenSchema = null;
39+
JsonSchema foundElseSchema = null;
40+
3741
for (final String keyword : KEYWORDS) {
3842
final JsonNode node = schemaNode.get(keyword);
3943
if (keyword.equals("if")) {
40-
ifSchema = new JsonSchema(validationContext, getValidatorType().getValue(), parentSchema.getCurrentUri(), node, parentSchema);
44+
foundIfSchema = new JsonSchema(validationContext, getValidatorType().getValue(), parentSchema.getCurrentUri(), node, parentSchema);
4145
} else if (keyword.equals("then") && node != null) {
42-
thenSchema = new JsonSchema(validationContext, getValidatorType().getValue(), parentSchema.getCurrentUri(), node, parentSchema);
46+
foundThenSchema = new JsonSchema(validationContext, getValidatorType().getValue(), parentSchema.getCurrentUri(), node, parentSchema);
4347
} else if (keyword.equals("else") && node != null) {
44-
elseSchema = new JsonSchema(validationContext, getValidatorType().getValue(), parentSchema.getCurrentUri(), node, parentSchema);
48+
foundElseSchema = new JsonSchema(validationContext, getValidatorType().getValue(), parentSchema.getCurrentUri(), node, parentSchema);
4549
}
4650
}
51+
52+
ifSchema = foundIfSchema;
53+
thenSchema = foundThenSchema;
54+
elseSchema = foundElseSchema;
4755
}
4856

4957
public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String at) {
@@ -61,4 +69,16 @@ public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String
6169
return Collections.unmodifiableSet(errors);
6270
}
6371

72+
@Override
73+
public void preloadJsonSchema() {
74+
if(null != ifSchema) {
75+
ifSchema.initializeValidators();
76+
}
77+
if(null != thenSchema) {
78+
thenSchema.initializeValidators();
79+
}
80+
if(null != elseSchema) {
81+
elseSchema.initializeValidators();
82+
}
83+
}
6484
}

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

+24-8
Original file line numberDiff line numberDiff line change
@@ -29,21 +29,24 @@ public class ItemsValidator extends BaseJsonValidator implements JsonValidator {
2929
private static final Logger logger = LoggerFactory.getLogger(ItemsValidator.class);
3030
private static final String PROPERTY_ADDITIONAL_ITEMS = "additionalItems";
3131

32-
private JsonSchema schema;
33-
private List<JsonSchema> tupleSchema;
32+
private final JsonSchema schema;
33+
private final List<JsonSchema> tupleSchema;
3434
private boolean additionalItems = true;
35-
private JsonSchema additionalSchema;
36-
private WalkListenerRunner arrayItemWalkListenerRunner;
37-
private ValidationContext validationContext;
35+
private final JsonSchema additionalSchema;
36+
private final WalkListenerRunner arrayItemWalkListenerRunner;
37+
private final ValidationContext validationContext;
3838

3939
public ItemsValidator(String schemaPath, JsonNode schemaNode, JsonSchema parentSchema,
4040
ValidationContext validationContext) {
4141
super(schemaPath, schemaNode, parentSchema, ValidatorTypeCode.ITEMS, validationContext);
42+
tupleSchema = new ArrayList<JsonSchema>();
43+
JsonSchema foundSchema = null;
44+
JsonSchema foundAdditionalSchema = null;
45+
4246
if (schemaNode.isObject() || schemaNode.isBoolean()) {
43-
schema = new JsonSchema(validationContext, schemaPath, parentSchema.getCurrentUri(), schemaNode,
47+
foundSchema = new JsonSchema(validationContext, schemaPath, parentSchema.getCurrentUri(), schemaNode,
4448
parentSchema);
4549
} else {
46-
tupleSchema = new ArrayList<JsonSchema>();
4750
for (JsonNode s : schemaNode) {
4851
tupleSchema.add(
4952
new JsonSchema(validationContext, schemaPath, parentSchema.getCurrentUri(), s, parentSchema));
@@ -54,7 +57,7 @@ public ItemsValidator(String schemaPath, JsonNode schemaNode, JsonSchema parentS
5457
if (addItemNode.isBoolean()) {
5558
additionalItems = addItemNode.asBoolean();
5659
} else if (addItemNode.isObject()) {
57-
additionalSchema = new JsonSchema(validationContext, parentSchema.getCurrentUri(), addItemNode);
60+
foundAdditionalSchema = new JsonSchema(validationContext, parentSchema.getCurrentUri(), addItemNode);
5861
}
5962
}
6063
}
@@ -63,6 +66,9 @@ public ItemsValidator(String schemaPath, JsonNode schemaNode, JsonSchema parentS
6366
this.validationContext = validationContext;
6467

6568
parseErrorCode(getValidatorType().getErrorCodeKey());
69+
70+
this.schema = foundSchema;
71+
this.additionalSchema = foundAdditionalSchema;
6672
}
6773

6874
public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String at) {
@@ -168,4 +174,14 @@ public JsonSchema getSchema() {
168174
return schema;
169175
}
170176

177+
@Override
178+
public void preloadJsonSchema() {
179+
if (null != schema) {
180+
schema.initializeValidators();
181+
}
182+
preloadJsonSchemas(tupleSchema);
183+
if (null != additionalSchema) {
184+
additionalSchema.initializeValidators();
185+
}
186+
}
171187
}

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

+28-9
Original file line numberDiff line numberDiff line change
@@ -16,21 +16,26 @@
1616

1717
package com.networknt.schema;
1818

19-
import com.fasterxml.jackson.databind.JsonNode;
20-
import com.fasterxml.jackson.databind.node.ObjectNode;
21-
import com.networknt.schema.ValidationContext.DiscriminatorContext;
22-
import com.networknt.schema.walk.DefaultKeywordWalkListenerRunner;
23-
import com.networknt.schema.walk.JsonSchemaWalker;
24-
import com.networknt.schema.walk.WalkListenerRunner;
25-
2619
import java.io.UnsupportedEncodingException;
2720
import java.net.URI;
2821
import java.net.URLDecoder;
29-
import java.util.*;
22+
import java.util.Collections;
23+
import java.util.HashMap;
24+
import java.util.Iterator;
25+
import java.util.LinkedHashSet;
26+
import java.util.Map;
3027
import java.util.Map.Entry;
28+
import java.util.Set;
3129
import java.util.regex.Matcher;
3230
import java.util.regex.Pattern;
3331

32+
import com.fasterxml.jackson.databind.JsonNode;
33+
import com.fasterxml.jackson.databind.node.ObjectNode;
34+
import com.networknt.schema.ValidationContext.DiscriminatorContext;
35+
import com.networknt.schema.walk.DefaultKeywordWalkListenerRunner;
36+
import com.networknt.schema.walk.JsonSchemaWalker;
37+
import com.networknt.schema.walk.WalkListenerRunner;
38+
3439
/**
3540
* This is the core of json constraint implementation. It parses json constraint
3641
* file and generates JsonValidators. The class is thread safe, once it is
@@ -253,7 +258,7 @@ public Set<ValidationMessage> validate(JsonNode jsonNode, JsonNode rootNode, Str
253258
// used for validation before allOf validation has kicked in
254259
discriminatorContext.registerDiscriminator(schemaPath, discriminator);
255260
discriminatorToUse = discriminator;
256-
} else{
261+
} else {
257262
discriminatorToUse = discriminatorFromContext;
258263
}
259264

@@ -408,4 +413,18 @@ public Map<String, JsonValidator> getValidators() {
408413
return validators;
409414
}
410415

416+
/**
417+
* Initializes the validators' {@link com.networknt.schema.JsonSchema} instances.
418+
* For avoiding issues with concurrency, in 1.0.49 the {@link com.networknt.schema.JsonSchema} instances affiliated with
419+
* validators were modified to no more preload the schema and lazy loading is used instead.
420+
* <p>This comes with the issue that this way you cannot rely on validating important schema features, in particular
421+
* <code>$ref</code> resolution at instantiation from {@link com.networknt.schema.JsonSchemaFactory}.</p>
422+
* <p>By calling <code>initializeValidators</code> you can enforce preloading of the {@link com.networknt.schema.JsonSchema}
423+
* instances of the validators.</p>
424+
*/
425+
public void initializeValidators() {
426+
for (final JsonValidator validator : getValidators().values()) {
427+
validator.preloadJsonSchema();
428+
}
429+
}
411430
}

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

+12-5
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,16 @@
1616

1717
package com.networknt.schema;
1818

19+
import java.util.Set;
20+
1921
import com.fasterxml.jackson.databind.JsonNode;
2022
import com.networknt.schema.walk.JsonSchemaWalker;
2123

22-
import java.util.Set;
23-
2424
/**
2525
* Standard json validator interface, implemented by all validators and JsonSchema.
2626
*/
2727
public interface JsonValidator extends JsonSchemaWalker {
28-
public static final String AT_ROOT = "$";
29-
28+
String AT_ROOT = "$";
3029

3130
/**
3231
* Validate the given root JsonNode, starting at the root of the data path.
@@ -48,6 +47,14 @@ public interface JsonValidator extends JsonSchemaWalker {
4847
* list if there is no error.
4948
*/
5049
Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String at);
51-
5250

51+
/**
52+
* In case the {@link com.networknt.schema.JsonValidator} has a related {@link com.networknt.schema.JsonSchema} or several
53+
* ones, calling preloadJsonSchema will actually load the schema document(s) eagerly.
54+
*
55+
* @throws JsonSchemaException (a {@link java.lang.RuntimeException}) in case the {@link com.networknt.schema.JsonSchema} or nested schemas
56+
* are invalid (like <code>$ref</code> not resolving)
57+
* @since 1.0.54
58+
*/
59+
void preloadJsonSchema() throws JsonSchemaException;
5360
}

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

+5
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,11 @@ private Validator(String keyword) {
3535
public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String at) {
3636
return Collections.emptySet();
3737
}
38+
39+
@Override
40+
public void preloadJsonSchema() {
41+
// not used and the Validator is not extending from BaseJsonValidator
42+
}
3843
}
3944

4045
public NonValidationKeyword(String keyword) {

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

+7-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
public class NotValidator extends BaseJsonValidator implements JsonValidator {
2828
private static final Logger logger = LoggerFactory.getLogger(RequiredValidator.class);
2929

30-
private JsonSchema schema;
30+
private final JsonSchema schema;
3131

3232
public NotValidator(String schemaPath, JsonNode schemaNode, JsonSchema parentSchema, ValidationContext validationContext) {
3333
super(schemaPath, schemaNode, parentSchema, ValidatorTypeCode.NOT, validationContext);
@@ -51,4 +51,10 @@ public Set<ValidationMessage> walk(JsonNode node, JsonNode rootNode, String at,
5151
return schema.walk(node, rootNode, at, shouldValidateSchema);
5252
}
5353

54+
@Override
55+
public void preloadJsonSchema() {
56+
if (null != schema) {
57+
schema.initializeValidators();
58+
}
59+
}
5460
}

0 commit comments

Comments
 (0)