Repository: networknt/json-schema-validator
Pull request #445 (2021-09-09 10:42:56)

JsonValidator: mark `preloadJsonSchema` as default
`JsonValidator#preloadJsonSchema` is not required so there is no point in forcing it to be implemented in each validator. 

I came across the fact that I need to implement this method in my custom validator. It looks like this is not required, so let's mark it as the default value.


Patch:
diff --git a/src/main/java/com/networknt/schema/AllOfValidator.java b/src/main/java/com/networknt/schema/AllOfValidator.java
index f1312bf..79ee22c 100644
--- a/src/main/java/com/networknt/schema/AllOfValidator.java
+++ b/src/main/java/com/networknt/schema/AllOfValidator.java
@@ -55,7 +55,7 @@ public class AllOfValidator extends BaseJsonValidator implements JsonValidator {
         for (JsonSchema schema : schemas) {
             errors.addAll(schema.validate(node, rootNode, at));
 
-            if (config.isOpenAPI3StyleDiscriminators()) {
+            if (this.validationContext.getConfig().isOpenAPI3StyleDiscriminators()) {
                 final Iterator<JsonNode> arrayElements = schemaNode.elements();
                 while (arrayElements.hasNext()) {
                     final ObjectNode allOfEntry = (ObjectNode) arrayElements.next();
diff --git a/src/main/java/com/networknt/schema/AnyOfValidator.java b/src/main/java/com/networknt/schema/AnyOfValidator.java
index 5b1a471..15edf5c 100644
--- a/src/main/java/com/networknt/schema/AnyOfValidator.java
+++ b/src/main/java/com/networknt/schema/AnyOfValidator.java
@@ -37,13 +37,13 @@ public class AnyOfValidator extends BaseJsonValidator implements JsonValidator {
         int size = schemaNode.size();
         for (int i = 0; i < size; i++) {
             schemas.add(new JsonSchema(validationContext,
-                                       getValidatorType().getValue(),
-                                       parentSchema.getCurrentUri(),
-                                       schemaNode.get(i),
-                                       parentSchema));
+                    getValidatorType().getValue(),
+                    parentSchema.getCurrentUri(),
+                    schemaNode.get(i),
+                    parentSchema));
         }
 
-        if (config.isOpenAPI3StyleDiscriminators()) {
+        if (this.validationContext.getConfig().isOpenAPI3StyleDiscriminators()) {
             this.discriminatorContext = new ValidationContext.DiscriminatorContext();
         } else {
             this.discriminatorContext = null;
@@ -53,7 +53,7 @@ public class AnyOfValidator extends BaseJsonValidator implements JsonValidator {
     public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String at) {
         debug(logger, node, rootNode, at);
 
-        if (config.isOpenAPI3StyleDiscriminators()) {
+        if (this.validationContext.getConfig().isOpenAPI3StyleDiscriminators()) {
             validationContext.enterDiscriminatorContext(this.discriminatorContext, at);
         }
 
@@ -72,9 +72,9 @@ public class AnyOfValidator extends BaseJsonValidator implements JsonValidator {
                     }
                 }
                 Set<ValidationMessage> errors = schema.validate(node, rootNode, at);
-                if (errors.isEmpty() && !config.isOpenAPI3StyleDiscriminators()) {
+                if (errors.isEmpty() && (!this.validationContext.getConfig().isOpenAPI3StyleDiscriminators())) {
                     return errors;
-                } else if (config.isOpenAPI3StyleDiscriminators()) {
+                } else if (this.validationContext.getConfig().isOpenAPI3StyleDiscriminators()) {
                     if (discriminatorContext.isDiscriminatorMatchFound()) {
                         if (!errors.isEmpty()) {
                             errors.add(buildValidationMessage(at, DISCRIMINATOR_REMARK));
@@ -85,13 +85,13 @@ public class AnyOfValidator extends BaseJsonValidator implements JsonValidator {
                 allErrors.addAll(errors);
             }
 
-            if (config.isOpenAPI3StyleDiscriminators() && discriminatorContext.isActive()) {
-               final Set<ValidationMessage> errors = new HashSet<ValidationMessage>();
-               errors.add(buildValidationMessage(at, "based on the provided discriminator. No alternative could be chosen based on the discriminator property"));
-               return Collections.unmodifiableSet(errors);
+            if (this.validationContext.getConfig().isOpenAPI3StyleDiscriminators() && discriminatorContext.isActive()) {
+                final Set<ValidationMessage> errors = new HashSet<ValidationMessage>();
+                errors.add(buildValidationMessage(at, "based on the provided discriminator. No alternative could be chosen based on the discriminator property"));
+                return Collections.unmodifiableSet(errors);
             }
         } finally {
-            if (config.isOpenAPI3StyleDiscriminators()) {
+            if (this.validationContext.getConfig().isOpenAPI3StyleDiscriminators()) {
                 validationContext.leaveDiscriminatorContextImmediately(at);
             }
         }
diff --git a/src/main/java/com/networknt/schema/BaseJsonValidator.java b/src/main/java/com/networknt/schema/BaseJsonValidator.java
index 9ad401b..3aa9a0b 100644
--- a/src/main/java/com/networknt/schema/BaseJsonValidator.java
+++ b/src/main/java/com/networknt/schema/BaseJsonValidator.java
@@ -36,17 +36,13 @@ public abstract class BaseJsonValidator implements JsonValidator {
     private boolean suppressSubSchemaRetrieval;
     private ValidatorTypeCode validatorType;
     private ErrorMessageType errorMessageType;
-    /**
-     * SchemaValidatorsConfig can only get and set in validationContext
-     */
-    protected SchemaValidatorsConfig config;
+
     protected final boolean failFast;
 
     public BaseJsonValidator(String schemaPath, JsonNode schemaNode, JsonSchema parentSchema,
                              ValidatorTypeCode validatorType, ValidationContext validationContext) {
         this(schemaPath, schemaNode, parentSchema, validatorType, false,
              validationContext.getConfig() != null && validationContext.getConfig().isFailFast());
-        this.config = validationContext.getConfig() == null ? new SchemaValidatorsConfig() : validationContext.getConfig();
     }
 
     public BaseJsonValidator(String schemaPath, JsonNode schemaNode, JsonSchema parentSchema,
@@ -165,11 +161,6 @@ public abstract class BaseJsonValidator implements JsonValidator {
         return validationMessages;
     }
 
-    @Override
-    public void preloadJsonSchema() {
-        // do nothing by default - to be overridden in subclasses
-    }
-
     protected void preloadJsonSchemas(final Collection<JsonSchema> schemas) {
         for (final JsonSchema schema: schemas) {
             schema.initializeValidators();
diff --git a/src/main/java/com/networknt/schema/DateTimeValidator.java b/src/main/java/com/networknt/schema/DateTimeValidator.java
index ed4d01f..420aaac 100644
--- a/src/main/java/com/networknt/schema/DateTimeValidator.java
+++ b/src/main/java/com/networknt/schema/DateTimeValidator.java
@@ -32,6 +32,8 @@ import java.util.regex.Pattern;
 public class DateTimeValidator extends BaseJsonValidator implements JsonValidator {
     private static final Logger logger = LoggerFactory.getLogger(DateTimeValidator.class);
 
+    private final ValidationContext validationContext;
+
     private final String formatName;
     private final String DATE = "date";
     private final String DATETIME = "date-time";
@@ -44,6 +46,7 @@ public class DateTimeValidator extends BaseJsonValidator implements JsonValidato
     public DateTimeValidator(String schemaPath, JsonNode schemaNode, JsonSchema parentSchema, ValidationContext validationContext, String formatName) {
         super(schemaPath, schemaNode, parentSchema, ValidatorTypeCode.DATETIME, validationContext);
         this.formatName = formatName;
+        this.validationContext = validationContext;
         parseErrorCode(getValidatorType().getErrorCodeKey());
     }
 
@@ -52,7 +55,7 @@ public class DateTimeValidator extends BaseJsonValidator implements JsonValidato
 
         Set<ValidationMessage> errors = new LinkedHashSet<ValidationMessage>();
 
-        JsonType nodeType = TypeFactory.getValueNodeType(node, super.config);
+        JsonType nodeType = TypeFactory.getValueNodeType(node, this.validationContext.getConfig());
         if (nodeType != JsonType.STRING) {
             return errors;
         }
diff --git a/src/main/java/com/networknt/schema/EnumValidator.java b/src/main/java/com/networknt/schema/EnumValidator.java
index abd95f9..dc24de9 100644
--- a/src/main/java/com/networknt/schema/EnumValidator.java
+++ b/src/main/java/com/networknt/schema/EnumValidator.java
@@ -32,10 +32,11 @@ public class EnumValidator extends BaseJsonValidator implements JsonValidator {
 
     private final Set<JsonNode> nodes;
     private final String error;
+    private final ValidationContext validationContext;
 
     public EnumValidator(String schemaPath, JsonNode schemaNode, JsonSchema parentSchema, ValidationContext validationContext) {
         super(schemaPath, schemaNode, parentSchema, ValidatorTypeCode.ENUM, validationContext);
-
+        this.validationContext = validationContext;
         if (schemaNode != null && schemaNode.isArray()) {
             nodes = new HashSet<JsonNode>();
             StringBuilder sb = new StringBuilder();
@@ -57,7 +58,7 @@ public class EnumValidator extends BaseJsonValidator implements JsonValidator {
             }
 
             // check if the parent schema declares the fields as nullable
-            if (config.isHandleNullableField()) {
+            if (validationContext.getConfig().isHandleNullableField()) {
                 JsonNode nullable = parentSchema.getSchemaNode().get("nullable");
                 if (nullable != null && nullable.asBoolean()) {
                     nodes.add(NullNode.getInstance());
@@ -83,7 +84,7 @@ public class EnumValidator extends BaseJsonValidator implements JsonValidator {
 
         Set<ValidationMessage> errors = new LinkedHashSet<ValidationMessage>();
         if (node.isNumber()) node = DecimalNode.valueOf(node.decimalValue());
-        if (!nodes.contains(node) && !(config.isTypeLoose() && isTypeLooseContainsInEnum(node))) {
+        if (!nodes.contains(node) && !( this.validationContext.getConfig().isTypeLoose() && isTypeLooseContainsInEnum(node))) {
             errors.add(buildValidationMessage(at, error));
         }
 
@@ -96,7 +97,7 @@ public class EnumValidator extends BaseJsonValidator implements JsonValidator {
      * @param node JsonNode to check
      */
     private boolean isTypeLooseContainsInEnum(JsonNode node) {
-        if (TypeFactory.getValueNodeType(node, super.config) == JsonType.STRING) {
+        if (TypeFactory.getValueNodeType(node, this.validationContext.getConfig()) == JsonType.STRING) {
             String nodeText = node.textValue();
             for (JsonNode n : nodes) {
                 String value = n.asText();
diff --git a/src/main/java/com/networknt/schema/ExclusiveMaximumValidator.java b/src/main/java/com/networknt/schema/ExclusiveMaximumValidator.java
index cf2942d..b9f7d44 100644
--- a/src/main/java/com/networknt/schema/ExclusiveMaximumValidator.java
+++ b/src/main/java/com/networknt/schema/ExclusiveMaximumValidator.java
@@ -30,9 +30,11 @@ public class ExclusiveMaximumValidator extends BaseJsonValidator implements Json
 
     private final ThresholdMixin typedMaximum;
 
+    private final ValidationContext validationContext;
+
     public ExclusiveMaximumValidator(String schemaPath, final JsonNode schemaNode, JsonSchema parentSchema, ValidationContext validationContext) {
         super(schemaPath, schemaNode, parentSchema, ValidatorTypeCode.EXCLUSIVE_MAXIMUM, validationContext);
-
+        this.validationContext = validationContext;
         if (!schemaNode.isNumber()) {
             throw new JsonSchemaException("exclusiveMaximum value is not a number");
         }
@@ -99,7 +101,7 @@ public class ExclusiveMaximumValidator extends BaseJsonValidator implements Json
     public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String at) {
         debug(logger, node, rootNode, at);
 
-        if (!TypeValidator.isNumber(node, super.config)) {
+        if (!TypeValidator.isNumber(node, validationContext.getConfig())) {
             // maximum only applies to numbers
             return Collections.emptySet();
         }
diff --git a/src/main/java/com/networknt/schema/ExclusiveMinimumValidator.java b/src/main/java/com/networknt/schema/ExclusiveMinimumValidator.java
index afccbfe..1444a2d 100644
--- a/src/main/java/com/networknt/schema/ExclusiveMinimumValidator.java
+++ b/src/main/java/com/networknt/schema/ExclusiveMinimumValidator.java
@@ -34,9 +34,11 @@ public class ExclusiveMinimumValidator extends BaseJsonValidator implements Json
      */
     private final ThresholdMixin typedMinimum;
 
+    private final ValidationContext validationContext;
+
     public ExclusiveMinimumValidator(String schemaPath, final JsonNode schemaNode, JsonSchema parentSchema, ValidationContext validationContext) {
         super(schemaPath, schemaNode, parentSchema, ValidatorTypeCode.EXCLUSIVE_MINIMUM, validationContext);
-
+        this.validationContext = validationContext;
         if (!schemaNode.isNumber()) {
             throw new JsonSchemaException("exclusiveMinimum value is not a number");
         }
@@ -106,7 +108,7 @@ public class ExclusiveMinimumValidator extends BaseJsonValidator implements Json
     public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String at) {
         debug(logger, node, rootNode, at);
 
-        if (!TypeValidator.isNumber(node, super.config)) {
+        if (!TypeValidator.isNumber(node, this.validationContext.getConfig())) {
             // minimum only applies to numbers
             return Collections.emptySet();
         }
diff --git a/src/main/java/com/networknt/schema/FormatValidator.java b/src/main/java/com/networknt/schema/FormatValidator.java
index 6db5f01..7e7962d 100644
--- a/src/main/java/com/networknt/schema/FormatValidator.java
+++ b/src/main/java/com/networknt/schema/FormatValidator.java
@@ -29,10 +29,12 @@ public class FormatValidator extends BaseJsonValidator implements JsonValidator
     private static final Logger logger = LoggerFactory.getLogger(FormatValidator.class);
 
     private final Format format;
+    private final ValidationContext validationContext;
 
     public FormatValidator(String schemaPath, JsonNode schemaNode, JsonSchema parentSchema, ValidationContext validationContext, Format format) {
         super(schemaPath, schemaNode, parentSchema, ValidatorTypeCode.FORMAT, validationContext);
         this.format = format;
+        this.validationContext = validationContext;
         parseErrorCode(getValidatorType().getErrorCodeKey());
     }
 
@@ -41,7 +43,7 @@ public class FormatValidator extends BaseJsonValidator implements JsonValidator
 
         Set<ValidationMessage> errors = new LinkedHashSet<ValidationMessage>();
 
-        JsonType nodeType = TypeFactory.getValueNodeType(node, super.config);
+        JsonType nodeType = TypeFactory.getValueNodeType(node, this.validationContext.getConfig());
         if (nodeType != JsonType.STRING) {
             return errors;
         }
diff --git a/src/main/java/com/networknt/schema/ItemsValidator.java b/src/main/java/com/networknt/schema/ItemsValidator.java
index 6f8ed5a..3a81f56 100644
--- a/src/main/java/com/networknt/schema/ItemsValidator.java
+++ b/src/main/java/com/networknt/schema/ItemsValidator.java
@@ -33,7 +33,7 @@ public class ItemsValidator extends BaseJsonValidator implements JsonValidator {
     private final List<JsonSchema> tupleSchema;
     private boolean additionalItems = true;
     private final JsonSchema additionalSchema;
-    private final WalkListenerRunner arrayItemWalkListenerRunner;
+    private WalkListenerRunner arrayItemWalkListenerRunner;
     private final ValidationContext validationContext;
 
     public ItemsValidator(String schemaPath, JsonNode schemaNode, JsonSchema parentSchema,
@@ -61,7 +61,7 @@ public class ItemsValidator extends BaseJsonValidator implements JsonValidator {
                 }
             }
         }
-        arrayItemWalkListenerRunner = new DefaultItemWalkListenerRunner(config.getArrayItemWalkListeners());
+        arrayItemWalkListenerRunner = new DefaultItemWalkListenerRunner(validationContext.getConfig().getArrayItemWalkListeners());
 
         this.validationContext = validationContext;
 
@@ -76,7 +76,7 @@ public class ItemsValidator extends BaseJsonValidator implements JsonValidator {
 
         Set<ValidationMessage> errors = new LinkedHashSet<ValidationMessage>();
 
-        if (!node.isArray() && !config.isTypeLoose()) {
+        if (!node.isArray() && !this.validationContext.getConfig().isTypeLoose()) {
             // ignores non-arrays
             return errors;
         }
diff --git a/src/main/java/com/networknt/schema/JsonSchema.java b/src/main/java/com/networknt/schema/JsonSchema.java
index 18936b9..6799e4d 100644
--- a/src/main/java/com/networknt/schema/JsonSchema.java
+++ b/src/main/java/com/networknt/schema/JsonSchema.java
@@ -19,6 +19,7 @@ package com.networknt.schema;
 import java.io.UnsupportedEncodingException;
 import java.net.URI;
 import java.net.URLDecoder;
+import java.sql.Ref;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Iterator;
@@ -36,6 +37,8 @@ import com.networknt.schema.walk.DefaultKeywordWalkListenerRunner;
 import com.networknt.schema.walk.JsonSchemaWalker;
 import com.networknt.schema.walk.WalkListenerRunner;
 
+import javax.xml.validation.Schema;
+
 /**
  * This is the core of json constraint implementation. It parses json constraint
  * file and generates JsonValidators. The class is thread safe, once it is
@@ -46,7 +49,6 @@ public class JsonSchema extends BaseJsonValidator {
     private Map<String, JsonValidator> validators;
     private final String idKeyword;
     private final ValidationContext validationContext;
-    private WalkListenerRunner keywordWalkListenerRunner;
     private boolean validatorsLoaded = false;
 
     /**
@@ -80,13 +82,10 @@ public class JsonSchema extends BaseJsonValidator {
         super(schemaPath, schemaNode, parent, null, suppressSubSchemaRetrieval,
               validationContext.getConfig() != null && validationContext.getConfig().isFailFast());
         this.validationContext = validationContext;
-        this.config = validationContext.getConfig();
         this.idKeyword = validationContext.getMetaSchema().getIdKeyword();
         this.currentUri = this.combineCurrentUriWithIds(currentUri, schemaNode);
-        if (config != null) {
-            this.keywordWalkListenerRunner = new DefaultKeywordWalkListenerRunner(config.getKeywordWalkListenersMap());
-
-            if (config.isOpenAPI3StyleDiscriminators()) {
+        if (validationContext.getConfig() != null) {
+            if (validationContext.getConfig().isOpenAPI3StyleDiscriminators()) {
                 ObjectNode discriminator = (ObjectNode) schemaNode.get("discriminator");
                 if (null != discriminator && null != validationContext.getCurrentDiscriminatorContext()) {
                     validationContext.getCurrentDiscriminatorContext().registerDiscriminator(schemaPath, discriminator);
@@ -95,6 +94,10 @@ public class JsonSchema extends BaseJsonValidator {
         }
     }
 
+    ValidationContext getValidationContext() {
+        return this.validationContext;
+    }
+
     private URI combineCurrentUriWithIds(URI currentUri, JsonNode schemaNode) {
         final String id = validationContext.resolveSchemaId(schemaNode);
         if (id == null) {
@@ -257,6 +260,7 @@ public class JsonSchema extends BaseJsonValidator {
     /************************ START OF VALIDATE METHODS **********************************/
 
     public Set<ValidationMessage> validate(JsonNode jsonNode, JsonNode rootNode, String at) {
+        SchemaValidatorsConfig config = validationContext.getConfig();
         Set<ValidationMessage> errors = new LinkedHashSet<ValidationMessage>();
         // Get the collector context.
         getCollectorContext();
@@ -353,6 +357,7 @@ public class JsonSchema extends BaseJsonValidator {
     @Override
     public Set<ValidationMessage> walk(JsonNode node, JsonNode rootNode, String at, boolean shouldValidateSchema) {
         Set<ValidationMessage> validationMessages = new LinkedHashSet<ValidationMessage>();
+        WalkListenerRunner keywordWalkListenerRunner = new DefaultKeywordWalkListenerRunner(this.validationContext.getConfig().getKeywordWalkListenersMap());
         // Walk through all the JSONWalker's.
         for (Entry<String, JsonValidator> entry : getValidators().entrySet()) {
             JsonSchemaWalker jsonWalker = entry.getValue();
@@ -401,11 +406,12 @@ public class JsonSchema extends BaseJsonValidator {
     }
 
     public CollectorContext getCollectorContext() {
+        SchemaValidatorsConfig config = validationContext.getConfig();
         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();
+            if (config != null && config.getCollectorContext() != null) {
+                collectorContext = config.getCollectorContext();
             } else {
                 collectorContext = new CollectorContext();
             }
diff --git a/src/main/java/com/networknt/schema/JsonSchemaFactory.java b/src/main/java/com/networknt/schema/JsonSchemaFactory.java
index 8fe4c51..b8f4baf 100644
--- a/src/main/java/com/networknt/schema/JsonSchemaFactory.java
+++ b/src/main/java/com/networknt/schema/JsonSchemaFactory.java
@@ -338,8 +338,14 @@ public class JsonSchemaFactory {
                 throw new JsonSchemaException(e);
             }
 
-            if (uriSchemaCache.containsKey(mappedUri))
-                return uriSchemaCache.get(mappedUri);
+            if (uriSchemaCache.containsKey(mappedUri)) {
+                JsonSchema cachedUriSchema =  uriSchemaCache.get(mappedUri);
+                // This is important because if we use same JsonSchemaFactory for creating multiple JSONSchema instances,
+                // these schemas will be cached along with config. We have to replace the config for cached $ref references
+                // with the latest config.
+                cachedUriSchema.getValidationContext().setConfig(config);
+                return cachedUriSchema;
+            }
 
             try {
                 inputStream = this.uriFetcher.fetch(mappedUri);
diff --git a/src/main/java/com/networknt/schema/JsonValidator.java b/src/main/java/com/networknt/schema/JsonValidator.java
index 32e59a8..dafc279 100644
--- a/src/main/java/com/networknt/schema/JsonValidator.java
+++ b/src/main/java/com/networknt/schema/JsonValidator.java
@@ -56,5 +56,7 @@ public interface JsonValidator extends JsonSchemaWalker {
      * are invalid (like <code>$ref</code> not resolving)
      * @since 1.0.54
      */
-    void preloadJsonSchema() throws JsonSchemaException;
+    default void preloadJsonSchema() throws JsonSchemaException {
+        // do nothing by default - to be overridden in subclasses
+    }
 }
diff --git a/src/main/java/com/networknt/schema/MaxItemsValidator.java b/src/main/java/com/networknt/schema/MaxItemsValidator.java
index dd17790..97a6812 100644
--- a/src/main/java/com/networknt/schema/MaxItemsValidator.java
+++ b/src/main/java/com/networknt/schema/MaxItemsValidator.java
@@ -24,8 +24,11 @@ import java.util.Collections;
 import java.util.Set;
 
 public class MaxItemsValidator extends BaseJsonValidator implements JsonValidator {
+
     private static final Logger logger = LoggerFactory.getLogger(MaxItemsValidator.class);
 
+    private final ValidationContext validationContext;
+
     private int max = 0;
 
     public MaxItemsValidator(String schemaPath, JsonNode schemaNode, JsonSchema parentSchema, ValidationContext validationContext) {
@@ -33,7 +36,7 @@ public class MaxItemsValidator extends BaseJsonValidator implements JsonValidato
         if (schemaNode.isIntegralNumber()) {
             max = schemaNode.intValue();
         }
-
+        this.validationContext = validationContext;
         parseErrorCode(getValidatorType().getErrorCodeKey());
     }
 
@@ -44,7 +47,7 @@ public class MaxItemsValidator extends BaseJsonValidator implements JsonValidato
             if (node.size() > max) {
                 return Collections.singleton(buildValidationMessage(at, "" + max));
             }
-        } else if (config.isTypeLoose()) {
+        } else if (this.validationContext.getConfig().isTypeLoose()) {
             if (1 > max) {
                 return Collections.singleton(buildValidationMessage(at, "" + max));
             }
diff --git a/src/main/java/com/networknt/schema/MaxLengthValidator.java b/src/main/java/com/networknt/schema/MaxLengthValidator.java
index e5c097f..0fbe761 100644
--- a/src/main/java/com/networknt/schema/MaxLengthValidator.java
+++ b/src/main/java/com/networknt/schema/MaxLengthValidator.java
@@ -26,6 +26,8 @@ import java.util.Set;
 public class MaxLengthValidator extends BaseJsonValidator implements JsonValidator {
     private static final Logger logger = LoggerFactory.getLogger(MaxLengthValidator.class);
 
+    private final ValidationContext validationContext;
+
     private int maxLength;
 
     public MaxLengthValidator(String schemaPath, JsonNode schemaNode, JsonSchema parentSchema, ValidationContext validationContext) {
@@ -34,14 +36,14 @@ public class MaxLengthValidator extends BaseJsonValidator implements JsonValidat
         if (schemaNode != null && schemaNode.isIntegralNumber()) {
             maxLength = schemaNode.intValue();
         }
-
+        this.validationContext = validationContext;
         parseErrorCode(getValidatorType().getErrorCodeKey());
     }
 
     public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String at) {
         debug(logger, node, rootNode, at);
 
-        JsonType nodeType = TypeFactory.getValueNodeType(node, super.config);
+        JsonType nodeType = TypeFactory.getValueNodeType(node, this.validationContext.getConfig());
         if (nodeType != JsonType.STRING) {
             // ignore no-string typs
             return Collections.emptySet();
diff --git a/src/main/java/com/networknt/schema/MaximumValidator.java b/src/main/java/com/networknt/schema/MaximumValidator.java
index b189bc7..9bd3a34 100644
--- a/src/main/java/com/networknt/schema/MaximumValidator.java
+++ b/src/main/java/com/networknt/schema/MaximumValidator.java
@@ -32,11 +32,12 @@ public class MaximumValidator extends BaseJsonValidator implements JsonValidator
     private boolean excludeEqual = false;
 
     private final ThresholdMixin typedMaximum;
+    private final ValidationContext validationContext;
 
 
     public MaximumValidator(String schemaPath, final JsonNode schemaNode, JsonSchema parentSchema, ValidationContext validationContext) {
         super(schemaPath, schemaNode, parentSchema, ValidatorTypeCode.MAXIMUM, validationContext);
-
+        this.validationContext = validationContext;
         if (!schemaNode.isNumber()) {
             throw new JsonSchemaException("maximum value is not a number");
         }
@@ -108,7 +109,7 @@ public class MaximumValidator extends BaseJsonValidator implements JsonValidator
     public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String at) {
         debug(logger, node, rootNode, at);
 
-        if (!TypeValidator.isNumber(node, super.config)) {
+        if (!TypeValidator.isNumber(node, this.validationContext.getConfig())) {
             // maximum only applies to numbers
             return Collections.emptySet();
         }
diff --git a/src/main/java/com/networknt/schema/MinItemsValidator.java b/src/main/java/com/networknt/schema/MinItemsValidator.java
index b58c5c0..dd84046 100644
--- a/src/main/java/com/networknt/schema/MinItemsValidator.java
+++ b/src/main/java/com/networknt/schema/MinItemsValidator.java
@@ -26,6 +26,8 @@ import java.util.Set;
 public class MinItemsValidator extends BaseJsonValidator implements JsonValidator {
     private static final Logger logger = LoggerFactory.getLogger(MinItemsValidator.class);
 
+    private final ValidationContext validationContext;
+
     private int min = 0;
 
     public MinItemsValidator(String schemaPath, JsonNode schemaNode, JsonSchema parentSchema, ValidationContext validationContext) {
@@ -33,7 +35,7 @@ public class MinItemsValidator extends BaseJsonValidator implements JsonValidato
         if (schemaNode.isIntegralNumber()) {
             min = schemaNode.intValue();
         }
-
+        this.validationContext = validationContext;
         parseErrorCode(getValidatorType().getErrorCodeKey());
     }
 
@@ -44,7 +46,7 @@ public class MinItemsValidator extends BaseJsonValidator implements JsonValidato
             if (node.size() < min) {
                 return Collections.singleton(buildValidationMessage(at, "" + min));
             }
-        } else if (config.isTypeLoose()) {
+        } else if (this.validationContext.getConfig().isTypeLoose()) {
             if (1 < min) {
                 return Collections.singleton(buildValidationMessage(at, "" + min));
             }
diff --git a/src/main/java/com/networknt/schema/MinLengthValidator.java b/src/main/java/com/networknt/schema/MinLengthValidator.java
index cec85eb..af88dd4 100644
--- a/src/main/java/com/networknt/schema/MinLengthValidator.java
+++ b/src/main/java/com/networknt/schema/MinLengthValidator.java
@@ -26,6 +26,8 @@ import java.util.Set;
 public class MinLengthValidator extends BaseJsonValidator implements JsonValidator {
     private static final Logger logger = LoggerFactory.getLogger(MinLengthValidator.class);
 
+    private final ValidationContext validationContext;
+
     private int minLength;
 
     public MinLengthValidator(String schemaPath, JsonNode schemaNode, JsonSchema parentSchema, ValidationContext validationContext) {
@@ -34,14 +36,14 @@ public class MinLengthValidator extends BaseJsonValidator implements JsonValidat
         if (schemaNode != null && schemaNode.isIntegralNumber()) {
             minLength = schemaNode.intValue();
         }
-
+        this.validationContext = validationContext;
         parseErrorCode(getValidatorType().getErrorCodeKey());
     }
 
     public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String at) {
         debug(logger, node, rootNode, at);
 
-        JsonType nodeType = TypeFactory.getValueNodeType(node, super.config);
+        JsonType nodeType = TypeFactory.getValueNodeType(node, this.validationContext.getConfig());
         if (nodeType != JsonType.STRING) {
             // ignore non-string types
             return Collections.emptySet();
diff --git a/src/main/java/com/networknt/schema/MinimumValidator.java b/src/main/java/com/networknt/schema/MinimumValidator.java
index f9af8f8..2208120 100644
--- a/src/main/java/com/networknt/schema/MinimumValidator.java
+++ b/src/main/java/com/networknt/schema/MinimumValidator.java
@@ -37,6 +37,8 @@ public class MinimumValidator extends BaseJsonValidator implements JsonValidator
      */
     private final ThresholdMixin typedMinimum;
 
+    private final ValidationContext validationContext;
+
     public MinimumValidator(String schemaPath, final JsonNode schemaNode, JsonSchema parentSchema, ValidationContext validationContext) {
         super(schemaPath, schemaNode, parentSchema, ValidatorTypeCode.MINIMUM, validationContext);
 
@@ -109,12 +111,13 @@ public class MinimumValidator extends BaseJsonValidator implements JsonValidator
                 }
             };
         }
+        this.validationContext = validationContext;
     }
 
     public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String at) {
         debug(logger, node, rootNode, at);
 
-        if (!TypeValidator.isNumber(node, super.config)) {
+        if (!TypeValidator.isNumber(node, this.validationContext.getConfig())) {
             // minimum only applies to numbers
             return Collections.emptySet();
         }
diff --git a/src/main/java/com/networknt/schema/NonValidationKeyword.java b/src/main/java/com/networknt/schema/NonValidationKeyword.java
index cdb7b7d..80271ac 100644
--- a/src/main/java/com/networknt/schema/NonValidationKeyword.java
+++ b/src/main/java/com/networknt/schema/NonValidationKeyword.java
@@ -35,11 +35,6 @@ public class NonValidationKeyword extends AbstractKeyword {
         public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String at) {
             return Collections.emptySet();
         }
-
-        @Override
-        public void preloadJsonSchema() {
-            // not used and the Validator is not extending from BaseJsonValidator
-        }
     }
 
     public NonValidationKeyword(String keyword) {
diff --git a/src/main/java/com/networknt/schema/OneOfValidator.java b/src/main/java/com/networknt/schema/OneOfValidator.java
index b44f5dd..283875e 100644
--- a/src/main/java/com/networknt/schema/OneOfValidator.java
+++ b/src/main/java/com/networknt/schema/OneOfValidator.java
@@ -27,13 +27,17 @@ import org.slf4j.LoggerFactory;
 import java.util.*;
 
 public class OneOfValidator extends BaseJsonValidator implements JsonValidator {
+
     private static final Logger logger = LoggerFactory.getLogger(OneOfValidator.class);
 
     private final List<ShortcutValidator> schemas = new ArrayList<ShortcutValidator>();
 
+    private final ValidationContext validationContext;
+
     private static class ShortcutValidator {
         private final JsonSchema schema;
         private final Map<String, String> constants;
+        private final ValidationContext validationContext;
 
         ShortcutValidator(JsonNode schemaNode, JsonSchema parentSchema,
                           ValidationContext validationContext, JsonSchema schema) {
@@ -41,6 +45,7 @@ public class OneOfValidator extends BaseJsonValidator implements JsonValidator {
             JsonSchema resolvedRefSchema = refNode != null && refNode.isTextual() ? RefValidator.getRefSchema(parentSchema, validationContext, refNode.textValue()).getSchema() : null;
             this.constants = extractConstants(schemaNode, resolvedRefSchema);
             this.schema = schema;
+            this.validationContext = validationContext;
         }
 
         private Map<String, String> extractConstants(JsonNode schemaNode, JsonSchema resolvedRefSchema) {
@@ -125,7 +130,7 @@ public class OneOfValidator extends BaseJsonValidator implements JsonValidator {
             JsonSchema childSchema = new JsonSchema(validationContext, getValidatorType().getValue(), parentSchema.getCurrentUri(), childNode, parentSchema);
             schemas.add(new ShortcutValidator(childNode, parentSchema, validationContext, childSchema));
         }
-
+        this.validationContext = validationContext;
         parseErrorCode(getValidatorType().getErrorCodeKey());
     }
 
@@ -159,7 +164,7 @@ public class OneOfValidator extends BaseJsonValidator implements JsonValidator {
             }*/
 
             //Check to see if it is already validated.
-            if(!childErrors.isEmpty() && JsonNodeUtil.matchOneOfTypeNode(schemaNode,TypeFactory.getValueNodeType(node, super.config))){
+            if(!childErrors.isEmpty() && JsonNodeUtil.matchOneOfTypeNode(schemaNode,TypeFactory.getValueNodeType(node, this.validationContext.getConfig()))){
                 continue;
             }
 
@@ -167,7 +172,8 @@ public class OneOfValidator extends BaseJsonValidator implements JsonValidator {
             JsonSchema schema = validator.schema;
 
             //Skip the validation when the current node is oneOf type and it is not equal to schemaType.
-            if(JsonNodeUtil.matchOneOfTypeNode(schemaNode,TypeFactory.getValueNodeType(node, super.config)) && !JsonNodeUtil.equalsToSchemaType(node,schema,config) && !(JsonType.UNKNOWN.equals(JsonNodeUtil.getSchemaJsonType(schema)))){
+            if (JsonNodeUtil.matchOneOfTypeNode(schemaNode, TypeFactory.getValueNodeType(node, this.validationContext.getConfig())) &&
+                    !JsonNodeUtil.equalsToSchemaType(node, schema, this.validationContext.getConfig()) && !(JsonType.UNKNOWN.equals(JsonNodeUtil.getSchemaJsonType(schema)))) {
                 continue;
             }
 
@@ -189,9 +195,11 @@ public class OneOfValidator extends BaseJsonValidator implements JsonValidator {
             childErrors.addAll(schemaErrors);
         }
         // ensure there is always an "OneOf" error reported if number of valid schemas is not equal to 1.
-        if(numberOfValidSchema > 1){
+        if (numberOfValidSchema > 1) {
             // check if the parent schema declares the fields as nullable
-            if (!JsonType.NULL.equals(TypeFactory.getValueNodeType(node,config)) || !JsonNodeUtil.isNodeNullable(parentSchema.getSchemaNode(),config) && !JsonNodeUtil.isChildNodeNullable((ArrayNode) schemaNode,config)) {
+            if (!JsonType.NULL.equals(TypeFactory.getValueNodeType(node, this.validationContext.getConfig())) ||
+                    !JsonNodeUtil.isNodeNullable(parentSchema.getSchemaNode(), this.validationContext.getConfig()) &&
+                            !JsonNodeUtil.isChildNodeNullable((ArrayNode) schemaNode, this.validationContext.getConfig())) {
                 final ValidationMessage message = getMultiSchemasValidErrorMsg(at);
                 if (failFast) {
                     throw new JsonSchemaException(message);
diff --git a/src/main/java/com/networknt/schema/PatternValidator.java b/src/main/java/com/networknt/schema/PatternValidator.java
index 36a7407..df6d13a 100644
--- a/src/main/java/com/networknt/schema/PatternValidator.java
+++ b/src/main/java/com/networknt/schema/PatternValidator.java
@@ -35,7 +35,10 @@ public class PatternValidator implements JsonValidator {
 
     private final JsonValidator delegate;
 
+    private final ValidationContext validationContext;
+
     public PatternValidator(String schemaPath, JsonNode schemaNode, JsonSchema parentSchema, ValidationContext validationContext) {
+        this.validationContext = validationContext;
          if (validationContext.getConfig() != null && validationContext.getConfig().isEcma262Validator()) {
              delegate = new PatternValidatorEcma262(schemaPath, schemaNode, parentSchema, validationContext);
          } else {
@@ -60,7 +63,7 @@ public class PatternValidator implements JsonValidator {
 
     private static class PatternValidatorJava extends BaseJsonValidator implements JsonValidator {
         private static final Logger logger = LoggerFactory.getLogger(PatternValidator.class);
-
+        private final ValidationContext validationContext;
         private String pattern;
         private Pattern compiledPattern;
 
@@ -77,7 +80,7 @@ public class PatternValidator implements JsonValidator {
                     throw pse;
                 }
             }
-
+            this.validationContext = validationContext;
             parseErrorCode(getValidatorType().getErrorCodeKey());
         }
 
@@ -88,7 +91,7 @@ public class PatternValidator implements JsonValidator {
         public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String at) {
             debug(logger, node, rootNode, at);
 
-            JsonType nodeType = TypeFactory.getValueNodeType(node, super.config);
+            JsonType nodeType = TypeFactory.getValueNodeType(node, this.validationContext.getConfig());
             if (nodeType != JsonType.STRING) {
                 return Collections.emptySet();
             }
@@ -107,7 +110,7 @@ public class PatternValidator implements JsonValidator {
 
     private static class PatternValidatorEcma262 extends BaseJsonValidator implements JsonValidator {
         private static final Logger logger = LoggerFactory.getLogger(PatternValidator.class);
-
+        private final ValidationContext validationContext;
         private String pattern;
         private Regex compiledRegex;
 
@@ -124,7 +127,7 @@ public class PatternValidator implements JsonValidator {
                     throw se;
                 }
             }
-
+            this.validationContext = validationContext;
             parseErrorCode(getValidatorType().getErrorCodeKey());
         }
 
@@ -145,7 +148,7 @@ public class PatternValidator implements JsonValidator {
         public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String at) {
             debug(logger, node, rootNode, at);
 
-            JsonType nodeType = TypeFactory.getValueNodeType(node, super.config);
+            JsonType nodeType = TypeFactory.getValueNodeType(node, validationContext.getConfig());
             if (nodeType != JsonType.STRING) {
                 return Collections.emptySet();
             }
@@ -161,9 +164,4 @@ public class PatternValidator implements JsonValidator {
             return Collections.emptySet();
         }
     }
-
-    @Override
-    public void preloadJsonSchema() {
-        // not used and the validator does not inherit from BaseJsonValidator
-    }
 }
diff --git a/src/main/java/com/networknt/schema/PropertiesValidator.java b/src/main/java/com/networknt/schema/PropertiesValidator.java
index 16b6adc..17c2016 100644
--- a/src/main/java/com/networknt/schema/PropertiesValidator.java
+++ b/src/main/java/com/networknt/schema/PropertiesValidator.java
@@ -28,7 +28,6 @@ public class PropertiesValidator extends BaseJsonValidator implements JsonValida
     public static final String PROPERTY = "properties";
     private static final Logger logger = LoggerFactory.getLogger(PropertiesValidator.class);
     private final Map<String, JsonSchema> schemas = new HashMap<String, JsonSchema>();
-    private final WalkListenerRunner propertyWalkListenerRunner;
     private final ValidationContext validationContext;
 
     public PropertiesValidator(String schemaPath, JsonNode schemaNode, JsonSchema parentSchema, ValidationContext validationContext) {
@@ -38,12 +37,13 @@ public class PropertiesValidator extends BaseJsonValidator implements JsonValida
             String pname = it.next();
             schemas.put(pname, new JsonSchema(validationContext, schemaPath + "/" + pname, parentSchema.getCurrentUri(), schemaNode.get(pname), parentSchema));
         }
-		propertyWalkListenerRunner = new DefaultPropertyWalkListenerRunner(config.getPropertyWalkListeners());
     }
 
     public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String at) {
         debug(logger, node, rootNode, at);
 
+        WalkListenerRunner propertyWalkListenerRunner = new DefaultPropertyWalkListenerRunner(this.validationContext.getConfig().getPropertyWalkListeners());
+
         Set<ValidationMessage> errors = new LinkedHashSet<ValidationMessage>();
 
         // get the Validator state object storing validation data
@@ -68,7 +68,7 @@ public class PropertiesValidator extends BaseJsonValidator implements JsonValida
                     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.isValidationEnabled(), errors);
+                    walkSchema(entry, node, rootNode, at, state.isValidationEnabled(), errors, propertyWalkListenerRunner);
                 }
 
                 // reset the complex flag to the original value before the recursive call
@@ -105,15 +105,16 @@ public class PropertiesValidator extends BaseJsonValidator implements JsonValida
         if (shouldValidateSchema) {
             validationMessages.addAll(validate(node, rootNode, at));
         } else {
+            WalkListenerRunner propertyWalkListenerRunner = new DefaultPropertyWalkListenerRunner(this.validationContext.getConfig().getPropertyWalkListeners());
             for (Map.Entry<String, JsonSchema> entry : schemas.entrySet()) {
-                walkSchema(entry, node, rootNode, at, shouldValidateSchema, validationMessages);
+                walkSchema(entry, node, rootNode, at, shouldValidateSchema, validationMessages, propertyWalkListenerRunner);
             }
         }
         return validationMessages;
     }
 
     private void walkSchema(Map.Entry<String, JsonSchema> entry, JsonNode node, JsonNode rootNode, String at,
-            boolean shouldValidateSchema, Set<ValidationMessage> validationMessages) {
+                            boolean shouldValidateSchema, Set<ValidationMessage> validationMessages, WalkListenerRunner propertyWalkListenerRunner) {
         JsonSchema propertySchema = entry.getValue();
         JsonNode propertyNode = (node == null ? null : node.get(entry.getKey()));
         boolean executeWalk = propertyWalkListenerRunner.runPreWalkListeners(ValidatorTypeCode.PROPERTIES.getValue(),
diff --git a/src/main/java/com/networknt/schema/RefValidator.java b/src/main/java/com/networknt/schema/RefValidator.java
index b08b986..74d2f73 100644
--- a/src/main/java/com/networknt/schema/RefValidator.java
+++ b/src/main/java/com/networknt/schema/RefValidator.java
@@ -34,11 +34,14 @@ public class RefValidator extends BaseJsonValidator implements JsonValidator {
 
     protected JsonSchemaRef schema;
 
+    private JsonSchema parentSchema;
+
     private static final String REF_CURRENT = "#";
 
     public RefValidator(String schemaPath, JsonNode schemaNode, JsonSchema parentSchema, ValidationContext validationContext) {
         super(schemaPath, schemaNode, parentSchema, ValidatorTypeCode.REF, validationContext);
         String refValue = schemaNode.asText();
+        this.parentSchema = parentSchema;
         schema = getRefSchema(parentSchema, validationContext, refValue);
         if (schema == null) {
             throw new JsonSchemaException(
@@ -128,7 +131,10 @@ public class RefValidator extends BaseJsonValidator implements JsonValidator {
 
     public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String at) {
         debug(logger, node, rootNode, at);
-
+        // This is important because if we use same JsonSchemaFactory for creating multiple JSONSchema instances,
+        // these schemas will be cached along with config. We have to replace the config for cached $ref references
+        // with the latest config. Reset the config.
+        schema.getSchema().getValidationContext().setConfig(parentSchema.getValidationContext().getConfig());
         if (schema != null) {
             return schema.validate(node, rootNode, at);
         } else {
@@ -138,6 +144,10 @@ public class RefValidator extends BaseJsonValidator implements JsonValidator {
 
 	@Override
 	public Set<ValidationMessage> walk(JsonNode node, JsonNode rootNode, String at, boolean shouldValidateSchema) {
+        // This is important because if we use same JsonSchemaFactory for creating multiple JSONSchema instances,
+        // these schemas will be cached along with config. We have to replace the config for cached $ref references
+        // with the latest config. Reset the config.
+        schema.getSchema().getValidationContext().setConfig(parentSchema.getValidationContext().getConfig());
 		if (schema != null) {
 			return schema.walk(node, rootNode, at, shouldValidateSchema);
 		}
diff --git a/src/main/java/com/networknt/schema/TypeFactory.java b/src/main/java/com/networknt/schema/TypeFactory.java
index 3e0693d..71bb8ea 100644
--- a/src/main/java/com/networknt/schema/TypeFactory.java
+++ b/src/main/java/com/networknt/schema/TypeFactory.java
@@ -72,9 +72,9 @@ public class TypeFactory {
             if (node.isIntegralNumber())
                 return JsonType.INTEGER;
             if (node.isNumber())
-                if (config.isJavaSemantics() && node.canConvertToLong() && (node.asText().indexOf('.') == -1))
+                if (config != null && config.isJavaSemantics() && node.canConvertToLong() && (node.asText().indexOf('.') == -1))
                     return JsonType.INTEGER;
-                else if (config.isLosslessNarrowing() && node.asText().endsWith(".0"))
+                else if (config!= null && config.isLosslessNarrowing() && node.asText().endsWith(".0"))
                     return JsonType.INTEGER;
                 else
                     return JsonType.NUMBER;
diff --git a/src/main/java/com/networknt/schema/TypeValidator.java b/src/main/java/com/networknt/schema/TypeValidator.java
index 8ca7173..5095083 100644
--- a/src/main/java/com/networknt/schema/TypeValidator.java
+++ b/src/main/java/com/networknt/schema/TypeValidator.java
@@ -37,12 +37,13 @@ public class TypeValidator extends BaseJsonValidator implements JsonValidator {
     private JsonType schemaType;
     private JsonSchema parentSchema;
     private UnionTypeValidator unionTypeValidator;
+    private final ValidationContext validationContext;
 
     public TypeValidator(String schemaPath, JsonNode schemaNode, JsonSchema parentSchema, ValidationContext validationContext) {
         super(schemaPath, schemaNode, parentSchema, ValidatorTypeCode.TYPE, validationContext);
         schemaType = TypeFactory.getSchemaNodeType(schemaNode);
         this.parentSchema = parentSchema;
-
+        this.validationContext = validationContext;
         if (schemaType == JsonType.UNION) {
             unionTypeValidator = new UnionTypeValidator(schemaPath, schemaNode, parentSchema, validationContext);
         }
@@ -55,7 +56,7 @@ public class TypeValidator extends BaseJsonValidator implements JsonValidator {
     }
 
     public boolean equalsToSchemaType(JsonNode node) {
-        JsonType nodeType = TypeFactory.getValueNodeType(node, super.config);
+        JsonType nodeType = TypeFactory.getValueNodeType(node, validationContext.getConfig());
         // in the case that node type is not the same as schema type, try to convert node to the
         // same type of schema. In REST API, query parameters, path parameters and headers are all
         // string type and we must convert, otherwise, all schema validations will fail.
@@ -70,7 +71,8 @@ public class TypeValidator extends BaseJsonValidator implements JsonValidator {
 
             ValidatorState state = (ValidatorState) CollectorContext.getInstance().get(ValidatorState.VALIDATOR_STATE_KEY);
             if(JsonType.NULL.equals(nodeType) ){
-                if((state.isComplexValidator() && JsonNodeUtil.isNodeNullable(parentSchema.getParentSchema().getSchemaNode(), config)) || JsonNodeUtil.isNodeNullable(this.getParentSchema().getSchemaNode()) ){
+                if ((state.isComplexValidator() && JsonNodeUtil.isNodeNullable(parentSchema.getParentSchema().getSchemaNode(), validationContext.getConfig())) ||
+                        JsonNodeUtil.isNodeNullable(this.getParentSchema().getSchemaNode())) {
                     return true;
                 }
             }
@@ -80,7 +82,7 @@ public class TypeValidator extends BaseJsonValidator implements JsonValidator {
             if (isEnumObjectSchema(parentSchema)) {
                 return true;
             }
-            if (config.isTypeLoose()) {
+            if (validationContext.getConfig().isTypeLoose()) {
                 // if typeLoose is true, everything can be a size 1 array
                 if (schemaType == JsonType.ARRAY) {
                     return true;
@@ -115,8 +117,8 @@ public class TypeValidator extends BaseJsonValidator implements JsonValidator {
         }
 
         //if (!equalsToSchemaType(node)) {
-        if(!JsonNodeUtil.equalsToSchemaType(node,schemaType, parentSchema, super.config)){
-            JsonType nodeType = TypeFactory.getValueNodeType(node, super.config);
+        if(!JsonNodeUtil.equalsToSchemaType(node,schemaType, parentSchema, validationContext.getConfig())){
+            JsonType nodeType = TypeFactory.getValueNodeType(node, validationContext.getConfig());
             return Collections.singleton(buildValidationMessage(at, nodeType.toString(), schemaType.toString()));
         }
         return Collections.emptySet();
diff --git a/src/main/java/com/networknt/schema/UUIDValidator.java b/src/main/java/com/networknt/schema/UUIDValidator.java
index 2677bef..1bb980c 100644
--- a/src/main/java/com/networknt/schema/UUIDValidator.java
+++ b/src/main/java/com/networknt/schema/UUIDValidator.java
@@ -31,11 +31,13 @@ public class UUIDValidator extends BaseJsonValidator implements JsonValidator {
 
 
     private static final String regex = "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$";
+    private final ValidationContext validationContext;
 
     public UUIDValidator(String schemaPath, JsonNode schemaNode, JsonSchema parentSchema, ValidationContext validationContext, String formatName) {
         super(schemaPath, schemaNode, parentSchema, ValidatorTypeCode.UUID, validationContext);
         this.formatName = formatName;
         parseErrorCode(getValidatorType().getErrorCodeKey());
+        this.validationContext = validationContext;
     }
 
     public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String at) {
@@ -43,7 +45,7 @@ public class UUIDValidator extends BaseJsonValidator implements JsonValidator {
 
         Set<ValidationMessage> errors = new LinkedHashSet<ValidationMessage>();
 
-        JsonType nodeType = TypeFactory.getValueNodeType(node, super.config);
+        JsonType nodeType = TypeFactory.getValueNodeType(node, this.validationContext.getConfig());
         if (nodeType != JsonType.STRING) {
             return errors;
         }
diff --git a/src/main/java/com/networknt/schema/UnionTypeValidator.java b/src/main/java/com/networknt/schema/UnionTypeValidator.java
index 547eafe..cb7326d 100644
--- a/src/main/java/com/networknt/schema/UnionTypeValidator.java
+++ b/src/main/java/com/networknt/schema/UnionTypeValidator.java
@@ -31,9 +31,11 @@ public class UnionTypeValidator extends BaseJsonValidator implements JsonValidat
     private final List<JsonValidator> schemas = new ArrayList<JsonValidator>();
     private final String error;
 
+    private final ValidationContext validationContext;
+
     public UnionTypeValidator(String schemaPath, JsonNode schemaNode, JsonSchema parentSchema, ValidationContext validationContext) {
         super(schemaPath, schemaNode, parentSchema, ValidatorTypeCode.UNION_TYPE, validationContext);
-
+        this.validationContext = validationContext;
         StringBuilder errorBuilder = new StringBuilder();
 
         String sep = "";
@@ -64,7 +66,7 @@ public class UnionTypeValidator extends BaseJsonValidator implements JsonValidat
     public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String at) {
         debug(logger, node, rootNode, at);
 
-        JsonType nodeType = TypeFactory.getValueNodeType(node, super.config);
+        JsonType nodeType = TypeFactory.getValueNodeType(node, validationContext.getConfig());
 
         boolean valid = false;
 
diff --git a/src/main/java/com/networknt/schema/ValidationContext.java b/src/main/java/com/networknt/schema/ValidationContext.java
index a7d2474..150cd51 100644
--- a/src/main/java/com/networknt/schema/ValidationContext.java
+++ b/src/main/java/com/networknt/schema/ValidationContext.java
@@ -74,6 +74,9 @@ public class ValidationContext {
     }
 
     public SchemaValidatorsConfig getConfig() {
+        if (config == null) {
+            config = new SchemaValidatorsConfig();
+        }
         return config;
     }
 
diff --git a/src/main/java/com/networknt/schema/format/EmailValidator.java b/src/main/java/com/networknt/schema/format/EmailValidator.java
index 57e9207..e69f183 100644
--- a/src/main/java/com/networknt/schema/format/EmailValidator.java
+++ b/src/main/java/com/networknt/schema/format/EmailValidator.java
@@ -62,6 +62,8 @@ public class EmailValidator extends BaseJsonValidator implements JsonValidator {
     private final boolean allowTld = false;
     private final String formatName;
 
+    private final ValidationContext validationContext;
+
     /**
      * <p>Checks if a field has a valid e-mail address.</p>
      *
@@ -138,6 +140,7 @@ public class EmailValidator extends BaseJsonValidator implements JsonValidator {
     public EmailValidator(String schemaPath, JsonNode schemaNode, JsonSchema parentSchema, ValidationContext validationContext, String formatName) {
         super(schemaPath, schemaNode, parentSchema, ValidatorTypeCode.FORMAT, validationContext);
         this.formatName = formatName;
+        this.validationContext = validationContext;
         parseErrorCode(getValidatorType().getErrorCodeKey());
     }
 
@@ -147,7 +150,7 @@ public class EmailValidator extends BaseJsonValidator implements JsonValidator {
 
         Set<ValidationMessage> errors = new LinkedHashSet<ValidationMessage>();
 
-        JsonType nodeType = TypeFactory.getValueNodeType(node, super.config);
+        JsonType nodeType = TypeFactory.getValueNodeType(node, this.validationContext.getConfig());
         if (nodeType != JsonType.STRING) {
             return errors;
         }
diff --git a/src/main/java/com/networknt/schema/utils/JsonNodeUtil.java b/src/main/java/com/networknt/schema/utils/JsonNodeUtil.java
index 141c6f2..d9ddd80 100644
--- a/src/main/java/com/networknt/schema/utils/JsonNodeUtil.java
+++ b/src/main/java/com/networknt/schema/utils/JsonNodeUtil.java
@@ -95,7 +95,7 @@ public class JsonNodeUtil {
             if (isEnumObjectSchema(parentSchema)) {
                 return true;
             }
-            if (config.isTypeLoose()) {
+            if (config != null && config.isTypeLoose()) {
                 // if typeLoose is true, everything can be a size 1 array
                 if (schemaType == JsonType.ARRAY) {
                     return true;


Test patch:
diff --git a/src/test/java/com/networknt/schema/CollectorContextTest.java b/src/test/java/com/networknt/schema/CollectorContextTest.java
index ec52b46..2e8827b 100644
--- a/src/test/java/com/networknt/schema/CollectorContextTest.java
+++ b/src/test/java/com/networknt/schema/CollectorContextTest.java
@@ -296,11 +296,6 @@ public class CollectorContextTest {
             // Ignore this method for testing.
             return null;
         }
-
-        @Override
-        public void preloadJsonSchema() {
-            // not used in testing
-        }
     }
 
     private class CustomCollector extends AbstractCollector<List<String>> {
@@ -378,11 +373,6 @@ public class CollectorContextTest {
             // Ignore this method for testing.
             return null;
         }
-
-        @Override
-        public void preloadJsonSchema() {
-            // not used in testing
-        }
     }
 
     private ValidationResult validate(String jsonData) throws JsonMappingException, JsonProcessingException, Exception {
diff --git a/src/test/java/com/networknt/schema/CustomMetaSchemaTest.java b/src/test/java/com/networknt/schema/CustomMetaSchemaTest.java
index fc18913..e2ee5e4 100644
--- a/src/test/java/com/networknt/schema/CustomMetaSchemaTest.java
+++ b/src/test/java/com/networknt/schema/CustomMetaSchemaTest.java
@@ -66,11 +66,6 @@ public class CustomMetaSchemaTest {
                 String valueName = enumNames.get(idx);
                 return fail(CustomErrorMessageType.of("tests.example.enumNames", new MessageFormat("{0}: enumName is {1}")), at, valueName);
             }
-
-            @Override
-            public void preloadJsonSchema() {
-                // not used in testing
-            }
         }
 
 
diff --git a/src/test/java/com/networknt/schema/JsonWalkTest.java b/src/test/java/com/networknt/schema/JsonWalkTest.java
index 8137bee..dc5b0e9 100644
--- a/src/test/java/com/networknt/schema/JsonWalkTest.java
+++ b/src/test/java/com/networknt/schema/JsonWalkTest.java
@@ -23,6 +23,8 @@ public class JsonWalkTest {
 
     private JsonSchema jsonSchema;
 
+    private JsonSchema jsonSchema1;
+
     private static final String SAMPLE_WALK_COLLECTOR_TYPE = "sampleWalkCollectorType";
 
     private static final String CUSTOM_KEYWORD = "custom-keyword";
@@ -39,6 +41,7 @@ public class JsonWalkTest {
 
     private void setupSchema() {
         final JsonMetaSchema metaSchema = getJsonMetaSchema();
+        // Create Schema.
         SchemaValidatorsConfig schemaValidatorsConfig = new SchemaValidatorsConfig();
         schemaValidatorsConfig.addKeywordWalkListener(new AllKeywordListener());
         schemaValidatorsConfig.addKeywordWalkListener(ValidatorTypeCode.REF.getValue(), new RefKeywordListener());
@@ -48,6 +51,12 @@ public class JsonWalkTest {
                 .builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V201909)).addMetaSchema(metaSchema)
                 .build();
         this.jsonSchema = schemaFactory.getSchema(getSchema(), schemaValidatorsConfig);
+        // Create another Schema.
+        SchemaValidatorsConfig schemaValidatorsConfig1 = new SchemaValidatorsConfig();
+        schemaValidatorsConfig1.addKeywordWalkListener(ValidatorTypeCode.REF.getValue(), new RefKeywordListener());
+        schemaValidatorsConfig1.addKeywordWalkListener(ValidatorTypeCode.PROPERTIES.getValue(),
+                new PropertiesKeywordListener());
+        this.jsonSchema1 = schemaFactory.getSchema(getSchema(), schemaValidatorsConfig1);
     }
 
     private JsonMetaSchema getJsonMetaSchema() {
@@ -75,6 +84,41 @@ public class JsonWalkTest {
                 + "}")));
     }
 
+    @Test
+    public void testWalkWithDifferentListeners() throws IOException {
+        ObjectMapper objectMapper = new ObjectMapper();
+        // This instance of schema contains all listeners.
+        ValidationResult result = jsonSchema.walk(
+                objectMapper.readTree(getClass().getClassLoader().getResourceAsStream("data/walk-data.json")), false);
+        JsonNode collectedNode = (JsonNode) result.getCollectorContext().get(SAMPLE_WALK_COLLECTOR_TYPE);
+        assertEquals(collectedNode, (objectMapper.readTree("{" +
+                "    \"PROPERTY1\": \"sample1\","
+                + "    \"PROPERTY2\": \"sample2\","
+                + "    \"property3\": {"
+                + "        \"street_address\":\"test-address\","
+                + "        \"phone_number\": {"
+                + "            \"country-code\": \"091\","
+                + "            \"number\": \"123456789\""
+                + "          }"
+                + "     }"
+                + "}")));
+        // This instance of schema contains one listener removed.
+        CollectorContext collectorContext = result.getCollectorContext();
+        collectorContext.reset();
+        result = jsonSchema1.walk(
+                objectMapper.readTree(getClass().getClassLoader().getResourceAsStream("data/walk-data.json")), false);
+        collectedNode = (JsonNode) result.getCollectorContext().get(SAMPLE_WALK_COLLECTOR_TYPE);
+        assertEquals(collectedNode, (objectMapper.readTree("{"
+                + "    \"property3\": {"
+                + "        \"street_address\":\"test-address\","
+                + "        \"phone_number\": {"
+                + "            \"country-code\": \"091\","
+                + "            \"number\": \"123456789\""
+                + "          }"
+                + "     }"
+                + "}")));
+    }
+
     private InputStream getSchema() {
         return getClass().getClassLoader().getResourceAsStream("schema/walk-schema.json");
     }
@@ -120,11 +164,6 @@ public class JsonWalkTest {
                                                boolean shouldValidateSchema) {
                 return new LinkedHashSet<ValidationMessage>();
             }
-
-            @Override
-            public void preloadJsonSchema() {
-                // not used in testing
-            }
         }
     }