Repository: networknt/json-schema-validator
Pull request #167 (2019-06-24 21:20:03)

Fix/#166 uri support
To summarize what I did:
- Lots of term changes because we no longer deal with just URLs.
- The JsonSchemaFactory.Builder is now able to be configured with URIFactories and URIFetchers. These factories and fetchers are able to be associated with a number of URI schemes. 
- A default URIFactory and URIFetcher is configured for our custom Classpath URLs and standard URLs. (These can be overridden if the user wishes).
- The JsonSchemaFactory accepts a URISchemeFactory and URISchemeFetcher that are configured with the scheme->factory/fetcher maps. The factory and fetchers are chosen based on the URI scheme that is needed.
- The URL mapping feature should now work with URIs in general.
- URIs are created using the JsonSchemaFactory's URIFactory (which is the URISchemeFactory).
- A URI's stream is fetched with the JsonSchemaFactory's URIFetcher (which is the URISchemeFetcher).

I'll make any changes that are necessary.


Patch:
diff --git a/.gitignore b/.gitignore
index 3901d72..a3dcd0c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -16,3 +16,4 @@ dist/
 
 # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
 hs_err_pid*
+/.gradle/
diff --git a/src/main/java/com/networknt/schema/AdditionalPropertiesValidator.java b/src/main/java/com/networknt/schema/AdditionalPropertiesValidator.java
index 2384f0a..c607241 100644
--- a/src/main/java/com/networknt/schema/AdditionalPropertiesValidator.java
+++ b/src/main/java/com/networknt/schema/AdditionalPropertiesValidator.java
@@ -47,7 +47,7 @@ public class AdditionalPropertiesValidator extends BaseJsonValidator implements
             additionalPropertiesSchema = null;
         } else if (schemaNode.isObject()) {
             allowAdditionalProperties = true;
-            additionalPropertiesSchema = new JsonSchema(validationContext, getValidatorType().getValue(), parentSchema.getCurrentUrl(), schemaNode, parentSchema);
+            additionalPropertiesSchema = new JsonSchema(validationContext, getValidatorType().getValue(), parentSchema.getCurrentUri(), schemaNode, parentSchema);
         } else {
             allowAdditionalProperties = false;
             additionalPropertiesSchema = null;
diff --git a/src/main/java/com/networknt/schema/AllOfValidator.java b/src/main/java/com/networknt/schema/AllOfValidator.java
index 52c2382..d45a8df 100644
--- a/src/main/java/com/networknt/schema/AllOfValidator.java
+++ b/src/main/java/com/networknt/schema/AllOfValidator.java
@@ -35,7 +35,7 @@ public class AllOfValidator extends BaseJsonValidator implements JsonValidator {
         super(schemaPath, schemaNode, parentSchema, ValidatorTypeCode.ALL_OF, validationContext);
         int size = schemaNode.size();
         for (int i = 0; i < size; i++) {
-            schemas.add(new JsonSchema(validationContext, getValidatorType().getValue(), parentSchema.getCurrentUrl(), schemaNode.get(i), parentSchema));
+            schemas.add(new JsonSchema(validationContext, getValidatorType().getValue(), parentSchema.getCurrentUri(), schemaNode.get(i), parentSchema));
         }
     }
 
diff --git a/src/main/java/com/networknt/schema/AnyOfValidator.java b/src/main/java/com/networknt/schema/AnyOfValidator.java
index 1d1a954..f4cca94 100644
--- a/src/main/java/com/networknt/schema/AnyOfValidator.java
+++ b/src/main/java/com/networknt/schema/AnyOfValidator.java
@@ -16,17 +16,17 @@
 
 package com.networknt.schema;
 
-import com.fasterxml.jackson.databind.JsonNode;
-import org.apache.commons.lang3.StringUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Set;
 
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.fasterxml.jackson.databind.JsonNode;
+
 public class AnyOfValidator extends BaseJsonValidator implements JsonValidator {
     private static final Logger logger = LoggerFactory.getLogger(RequiredValidator.class);
 
@@ -36,7 +36,7 @@ public class AnyOfValidator extends BaseJsonValidator implements JsonValidator {
         super(schemaPath, schemaNode, parentSchema, ValidatorTypeCode.ANY_OF, validationContext);
         int size = schemaNode.size();
         for (int i = 0; i < size; i++) {
-            schemas.add(new JsonSchema(validationContext, getValidatorType().getValue(), parentSchema.getCurrentUrl(), schemaNode.get(i), parentSchema));
+            schemas.add(new JsonSchema(validationContext, getValidatorType().getValue(), parentSchema.getCurrentUri(), schemaNode.get(i), parentSchema));
         }
     }
 
@@ -64,7 +64,7 @@ public class AnyOfValidator extends BaseJsonValidator implements JsonValidator {
             allErrors.addAll(errors);
         }
         if (!expectedTypeList.isEmpty()) {
-            return Collections.singleton(buildValidationMessage(at, StringUtils.join(expectedTypeList)));
+            return Collections.singleton(buildValidationMessage(at, expectedTypeList.toArray(new String[expectedTypeList.size()])));
         }
         return Collections.unmodifiableSet(allErrors);
     }
diff --git a/src/main/java/com/networknt/schema/BaseJsonValidator.java b/src/main/java/com/networknt/schema/BaseJsonValidator.java
index 41ac1fe..6945973 100644
--- a/src/main/java/com/networknt/schema/BaseJsonValidator.java
+++ b/src/main/java/com/networknt/schema/BaseJsonValidator.java
@@ -16,14 +16,13 @@
 
 package com.networknt.schema;
 
-import com.fasterxml.jackson.databind.JsonNode;
-import com.networknt.schema.url.URLFactory;
+import java.net.URI;
+import java.util.Set;
+
 import org.apache.commons.lang3.StringUtils;
 import org.slf4j.Logger;
 
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.util.Set;
+import com.fasterxml.jackson.databind.JsonNode;
 
 public abstract class BaseJsonValidator implements JsonValidator {
     private String schemaPath;
@@ -71,22 +70,23 @@ public abstract class BaseJsonValidator implements JsonValidator {
     }
 
     
-    private static JsonSchema obtainSubSchemaNode(JsonNode schemaNode, ValidationContext validationContext){
-        JsonNode node = schemaNode.get("id");
+    private static JsonSchema obtainSubSchemaNode(final JsonNode schemaNode, final ValidationContext validationContext){
+        final JsonNode node = schemaNode.get("id");
         if(node == null) return null;
     	if(node.equals(schemaNode.get("$schema"))) return null;
 
-        try {
-            String text = node.textValue();
-            if (text == null) {
+        final String text = node.textValue();
+        if (text == null) {
+            return null;
+        }
+        else {
+        	final URI uri;
+            try {
+                uri = validationContext.getJsonSchemaFactory().getURIFactory().create(node.textValue());
+            } catch (IllegalArgumentException e) {
                 return null;
             }
-            else {
-                URL url = URLFactory.toURL(node.textValue());
-                return validationContext.getJsonSchemaFactory().getSchema(url, validationContext.getConfig());
-            }
-        } catch (MalformedURLException e) {
-            return null;
+            return validationContext.getJsonSchemaFactory().getSchema(uri, validationContext.getConfig());
         }
     }
 
diff --git a/src/main/java/com/networknt/schema/DependenciesValidator.java b/src/main/java/com/networknt/schema/DependenciesValidator.java
index 1eb0168..bfba02b 100644
--- a/src/main/java/com/networknt/schema/DependenciesValidator.java
+++ b/src/main/java/com/networknt/schema/DependenciesValidator.java
@@ -44,7 +44,7 @@ public class DependenciesValidator extends BaseJsonValidator implements JsonVali
                     depsProps.add(pvalue.get(i).asText());
                 }
             } else if (pvalue.isObject()) {
-                schemaDeps.put(pname, new JsonSchema(validationContext, pname, parentSchema.getCurrentUrl(), pvalue, parentSchema));
+                schemaDeps.put(pname, new JsonSchema(validationContext, pname, parentSchema.getCurrentUri(), pvalue, parentSchema));
             }
         }
 
diff --git a/src/main/java/com/networknt/schema/ItemsValidator.java b/src/main/java/com/networknt/schema/ItemsValidator.java
index b164c58..d2c1015 100644
--- a/src/main/java/com/networknt/schema/ItemsValidator.java
+++ b/src/main/java/com/networknt/schema/ItemsValidator.java
@@ -38,11 +38,11 @@ public class ItemsValidator extends BaseJsonValidator implements JsonValidator {
     public ItemsValidator(String schemaPath, JsonNode schemaNode, JsonSchema parentSchema, ValidationContext validationContext) {
         super(schemaPath, schemaNode, parentSchema, ValidatorTypeCode.ITEMS, validationContext);
         if (schemaNode.isObject()) {
-            schema = new JsonSchema(validationContext, getValidatorType().getValue(), parentSchema.getCurrentUrl(), schemaNode, parentSchema);
+            schema = new JsonSchema(validationContext, getValidatorType().getValue(), parentSchema.getCurrentUri(), schemaNode, parentSchema);
         } else {
             tupleSchema = new ArrayList<JsonSchema>();
             for (JsonNode s : schemaNode) {
-                tupleSchema.add(new JsonSchema(validationContext, getValidatorType().getValue(), parentSchema.getCurrentUrl(), s, parentSchema));
+                tupleSchema.add(new JsonSchema(validationContext, getValidatorType().getValue(), parentSchema.getCurrentUri(), s, parentSchema));
             }
 
             JsonNode addItemNode = getParentSchema().getSchemaNode().get(PROPERTY_ADDITIONAL_ITEMS);
@@ -50,7 +50,7 @@ public class ItemsValidator extends BaseJsonValidator implements JsonValidator {
                 if (addItemNode.isBoolean()) {
                     additionalItems = addItemNode.asBoolean();
                 } else if (addItemNode.isObject()) {
-                    additionalSchema = new JsonSchema(validationContext, parentSchema.getCurrentUrl(), addItemNode);
+                    additionalSchema = new JsonSchema(validationContext, parentSchema.getCurrentUri(), addItemNode);
                 }
             }
         }
diff --git a/src/main/java/com/networknt/schema/JsonSchema.java b/src/main/java/com/networknt/schema/JsonSchema.java
index cafd6b7..9eea10d 100644
--- a/src/main/java/com/networknt/schema/JsonSchema.java
+++ b/src/main/java/com/networknt/schema/JsonSchema.java
@@ -17,8 +17,7 @@
 package com.networknt.schema;
 
 import java.io.UnsupportedEncodingException;
-import java.net.MalformedURLException;
-import java.net.URL;
+import java.net.URI;
 import java.net.URLDecoder;
 import java.util.Collections;
 import java.util.HashMap;
@@ -30,7 +29,6 @@ import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
 import com.fasterxml.jackson.databind.JsonNode;
-import com.networknt.schema.url.URLFactory;
 
 /**
  * This is the core of json constraint implementation. It parses json constraint
@@ -43,59 +41,56 @@ public class JsonSchema extends BaseJsonValidator {
     private final ValidationContext validationContext;
     
     /**
-     * This is the current url of this schema. This url could refer to the url of this schema's file
-     * or it could potentially be a url that has been altered by an id. An 'id' is able to completely overwrite
-     * the current url or add onto it. This is necessary so that '$ref's are able to be relative to a
-     * combination of the current schema file's url and 'id' urls visible to this schema.
+     * This is the current uri of this schema. This uri could refer to the uri of this schema's file
+     * or it could potentially be a uri that has been altered by an id. An 'id' is able to completely overwrite
+     * the current uri or add onto it. This is necessary so that '$ref's are able to be relative to a
+     * combination of the current schema file's uri and 'id' uris visible to this schema.
      * 
-     * This can be null. If it is null, then the creation of relative urls will fail. However, an absolute
-     * 'id' would still be able to specify an absolute url.
+     * This can be null. If it is null, then the creation of relative uris will fail. However, an absolute
+     * 'id' would still be able to specify an absolute uri.
      */
-    private final URL currentUrl;
+    private final URI currentUri;
     
     private JsonValidator requiredValidator = null;
 
-    public JsonSchema(ValidationContext validationContext, URL baseUrl, JsonNode schemaNode) {
-        this(validationContext, "#", baseUrl, schemaNode, null);
+    public JsonSchema(ValidationContext validationContext, URI baseUri, JsonNode schemaNode) {
+        this(validationContext, "#", baseUri, schemaNode, null);
     }
 
-    public JsonSchema(ValidationContext validationContext, String schemaPath, URL currentUrl, JsonNode schemaNode,
+    public JsonSchema(ValidationContext validationContext, String schemaPath, URI currentUri, JsonNode schemaNode,
                JsonSchema parent) {
-        this(validationContext,  schemaPath, currentUrl, schemaNode, parent, false);
+        this(validationContext,  schemaPath, currentUri, schemaNode, parent, false);
     }
 
-    public JsonSchema(ValidationContext validationContext, URL baseUrl, JsonNode schemaNode, boolean suppressSubSchemaRetrieval) {
-        this(validationContext, "#", baseUrl, schemaNode, null, suppressSubSchemaRetrieval);
+    public JsonSchema(ValidationContext validationContext, URI baseUri, JsonNode schemaNode, boolean suppressSubSchemaRetrieval) {
+        this(validationContext, "#", baseUri, schemaNode, null, suppressSubSchemaRetrieval);
     }
 
-    private JsonSchema(ValidationContext validationContext,  String schemaPath, URL currentUrl, JsonNode schemaNode,
+    private JsonSchema(ValidationContext validationContext,  String schemaPath, URI currentUri, JsonNode schemaNode,
                JsonSchema parent, boolean suppressSubSchemaRetrieval) {
         super(schemaPath, schemaNode, parent, null, suppressSubSchemaRetrieval);
         this.validationContext = validationContext;
         this.config = validationContext.getConfig();
-        this.currentUrl = this.combineCurrentUrlWithIds(currentUrl, schemaNode);
+        this.currentUri = this.combineCurrentUriWithIds(currentUri, schemaNode);
         this.validators = Collections.unmodifiableMap(this.read(schemaNode));
     }
     
-    private URL combineCurrentUrlWithIds(URL currentUrl, JsonNode schemaNode) {
+    private URI combineCurrentUriWithIds(URI currentUri, JsonNode schemaNode) {
       final JsonNode idNode = schemaNode.get("id");
       if (idNode == null) {
-        return currentUrl;
+        return currentUri;
       } else {
-        try
-        {
-          return URLFactory.toURL(currentUrl, idNode.asText());
-        }
-        catch (MalformedURLException e)
-        {
-          throw new IllegalArgumentException(String.format("Invalid 'id' in schema: %s", schemaNode), e);
+        try {
+          return this.validationContext.getJsonSchemaFactory().getURIFactory().create(currentUri, idNode.asText());
+        } catch (IllegalArgumentException e) {
+          throw new JsonSchemaException(ValidationMessage.of(ValidatorTypeCode.ID.getValue(), ValidatorTypeCode.ID, idNode.asText(), currentUri.toString()));
         }
       }
     }
     
-    public URL getCurrentUrl()
+    public URI getCurrentUri()
     {
-      return this.currentUrl;
+      return this.currentUri;
     }
 
     /**
diff --git a/src/main/java/com/networknt/schema/JsonSchemaFactory.java b/src/main/java/com/networknt/schema/JsonSchemaFactory.java
index 85a1963..7c1b2d5 100644
--- a/src/main/java/com/networknt/schema/JsonSchemaFactory.java
+++ b/src/main/java/com/networknt/schema/JsonSchemaFactory.java
@@ -18,7 +18,7 @@ package com.networknt.schema;
 
 import java.io.IOException;
 import java.io.InputStream;
-import java.net.URL;
+import java.net.URI;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.Map;
@@ -28,9 +28,14 @@ import org.slf4j.LoggerFactory;
 
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.ObjectMapper;
-import com.networknt.schema.url.StandardURLFetcher;
-import com.networknt.schema.url.URLFactory;
-import com.networknt.schema.url.URLFetcher;
+import com.networknt.schema.uri.ClasspathURLFactory;
+import com.networknt.schema.uri.ClasspathURLFetcher;
+import com.networknt.schema.uri.URIFactory;
+import com.networknt.schema.uri.URIFetcher;
+import com.networknt.schema.uri.URISchemeFactory;
+import com.networknt.schema.uri.URISchemeFetcher;
+import com.networknt.schema.uri.URLFactory;
+import com.networknt.schema.uri.URLFetcher;
 
 public class JsonSchemaFactory {
     private static final Logger logger = LoggerFactory
@@ -39,40 +44,86 @@ public class JsonSchemaFactory {
     
     public static class Builder {
         private ObjectMapper objectMapper = new ObjectMapper();
-        private URLFetcher urlFetcher;
         private String defaultMetaSchemaURI;
-        private Map<String, JsonMetaSchema> jsonMetaSchemas = new HashMap<String, JsonMetaSchema>();
-        private Map<String, String> urlMap = new HashMap<String, String>();
+        private final Map<String, URIFactory> uriFactoryMap = new HashMap<String, URIFactory>();
+        private final Map<String, URIFetcher> uriFetcherMap = new HashMap<String, URIFetcher>();
+        private final Map<String, JsonMetaSchema> jsonMetaSchemas = new HashMap<String, JsonMetaSchema>();
+        private final Map<String, String> uriMap = new HashMap<String, String>();
         
-        public Builder objectMapper(ObjectMapper objectMapper) {
+        public Builder() {
+            // Adds support for creating {@link URL}s.
+            final URIFactory urlFactory = new URLFactory();
+            for (final String scheme : URLFactory.SUPPORTED_SCHEMES) {
+                this.uriFactoryMap.put(scheme, urlFactory);
+            }
+
+            // Adds support for fetching with {@link URL}s.
+            final URIFetcher urlFetcher = new URLFetcher();
+            for (final String scheme : URLFetcher.SUPPORTED_SCHEMES) {
+                this.uriFetcherMap.put(scheme, urlFetcher);
+            }
+
+            // Adds support for creating and fetching with classpath {@link URL}s.
+            final URIFactory classpathURLFactory = new ClasspathURLFactory();
+            final URIFetcher classpathURLFetcher = new ClasspathURLFetcher();
+            for (final String scheme : ClasspathURLFactory.SUPPORTED_SCHEMES) {
+                this.uriFactoryMap.put(scheme, classpathURLFactory);
+                this.uriFetcherMap.put(scheme, classpathURLFetcher);
+            }
+        }
+        
+        public Builder objectMapper(final ObjectMapper objectMapper) {
             this.objectMapper = objectMapper;
             return this;
         }
         
-        public Builder urlFetcher(URLFetcher urlFetcher) {
-            this.urlFetcher = urlFetcher;
+        public Builder defaultMetaSchemaURI(final String defaultMetaSchemaURI) {
+            this.defaultMetaSchemaURI = defaultMetaSchemaURI;
             return this;
         }
         
-        public Builder defaultMetaSchemaURI(String defaultMetaSchemaURI) {
-            this.defaultMetaSchemaURI = defaultMetaSchemaURI;
+        /**
+         * Maps a number of schemes to a {@link URIFactory}.
+         * @param uriFactory the uri factory that will be used for the given schemes.
+         * @param scheme the scheme that the uri factory will be assocaited with.
+         * @return this builder.
+         */
+        public Builder uriFactory(final URIFactory uriFactory, final String... schemes) {
+            for (final String scheme : schemes)
+            {
+                this.uriFactoryMap.put(scheme, uriFactory);
+            }
+            return this;
+        }
+		
+        /**
+         * Maps a number of schemes to a {@link URIFetcher}.
+         * @param uriFetcher the uri fetcher that will be used for the given schemes.
+         * @param scheme the scheme that the uri fetcher will be assocaited with.
+         * @return this builder.
+         */
+        public Builder uriFetcher(final URIFetcher uriFetcher, final String... schemes) {
+            for (final String scheme : schemes)
+            {
+                this.uriFetcherMap.put(scheme, uriFetcher);
+            }
             return this;
         }
         
-        public Builder addMetaSchema(JsonMetaSchema jsonMetaSchema) {
+        public Builder addMetaSchema(final JsonMetaSchema jsonMetaSchema) {
             this.jsonMetaSchemas.put(jsonMetaSchema.getUri(), jsonMetaSchema);
             return this;
         }
         
-        public Builder addMetaSchemas(Collection<? extends JsonMetaSchema> jsonMetaSchemas) {
+        public Builder addMetaSchemas(final Collection<? extends JsonMetaSchema> jsonMetaSchemas) {
             for (JsonMetaSchema jsonMetaSchema: jsonMetaSchemas) {
                 this.jsonMetaSchemas.put(jsonMetaSchema.getUri(), jsonMetaSchema);
             }
             return this;
         }
         
-        public Builder addUrlMappings(Map<String, String> map) {
-            this.urlMap.putAll(map);
+        public Builder addUriMappings(final Map<String, String> map) {
+            this.uriMap.putAll(map);
             return this;
         }
         
@@ -80,49 +131,56 @@ public class JsonSchemaFactory {
             // create builtin keywords with (custom) formats.
             return new JsonSchemaFactory(
                     objectMapper == null ? new ObjectMapper() : objectMapper, 
-                    urlFetcher == null ? new StandardURLFetcher(): urlFetcher, 
                     defaultMetaSchemaURI, 
+                    new URISchemeFactory(uriFactoryMap),
+                    new URISchemeFetcher(uriFetcherMap), 
                     jsonMetaSchemas,
-                    urlMap
+                    uriMap
             );
         }
     }
     
     private final ObjectMapper mapper;
-    private final URLFetcher urlFetcher;
     private final String defaultMetaSchemaURI;
+    private final URISchemeFactory uriFactory;
+    private final URISchemeFetcher uriFetcher;
     private final Map<String, JsonMetaSchema> jsonMetaSchemas;
-    private final Map<String, String> urlMap;
+    private final Map<String, String> uriMap;
 
     private JsonSchemaFactory(
-            ObjectMapper mapper, 
-            URLFetcher urlFetcher, 
-            String defaultMetaSchemaURI, 
-            Map<String, JsonMetaSchema> jsonMetaSchemas, 
-            Map<String, String> urlMap) {
+            final ObjectMapper mapper, 
+            final String defaultMetaSchemaURI, 
+            final URISchemeFactory uriFactory, 
+            final URISchemeFetcher uriFetcher, 
+            final Map<String, JsonMetaSchema> jsonMetaSchemas, 
+            final Map<String, String> uriMap) {
         if (mapper == null) {
             throw new IllegalArgumentException("ObjectMapper must not be null");
         }
-        if (urlFetcher == null) {
-            throw new IllegalArgumentException("URLFetcher must not be null");
-        }
         if (defaultMetaSchemaURI == null || defaultMetaSchemaURI.trim().isEmpty()) {
             throw new IllegalArgumentException("defaultMetaSchemaURI must not be null or empty");
         }
+        if (uriFactory == null) {
+            throw new IllegalArgumentException("URIFactory must not be null");
+        }
+        if (uriFetcher == null) {
+            throw new IllegalArgumentException("URIFetcher must not be null");
+        }
         if (jsonMetaSchemas == null || jsonMetaSchemas.isEmpty()) {
             throw new IllegalArgumentException("Json Meta Schemas must not be null or empty");
         }
         if (jsonMetaSchemas.get(defaultMetaSchemaURI) == null) {
             throw new IllegalArgumentException("Meta Schema for default Meta Schema URI must be provided");
         }
-        if (urlMap == null) {
+        if (uriMap == null) {
             throw new IllegalArgumentException("URL Mappings must not be null");
         }
         this.mapper = mapper;
         this.defaultMetaSchemaURI = defaultMetaSchemaURI;
-        this.urlFetcher = urlFetcher;
+        this.uriFactory = uriFactory;
+        this.uriFetcher = uriFetcher;
         this.jsonMetaSchemas = jsonMetaSchemas;
-        this.urlMap = urlMap;
+        this.uriMap = uriMap;
     }
 
     /**
@@ -148,28 +206,37 @@ public class JsonSchemaFactory {
                 .build();
     }
     
-    public static Builder builder(JsonSchemaFactory blueprint) {
-        return builder()
+    public static Builder builder(final JsonSchemaFactory blueprint) {
+        Builder builder = builder()
                 .addMetaSchemas(blueprint.jsonMetaSchemas.values())
-                .urlFetcher(blueprint.urlFetcher)
                 .defaultMetaSchemaURI(blueprint.defaultMetaSchemaURI)
                 .objectMapper(blueprint.mapper)
-                .addUrlMappings(blueprint.urlMap);
+                .addUriMappings(blueprint.uriMap);
+        
+        for (Map.Entry<String, URIFactory> entry : blueprint.uriFactory.getURIFactories().entrySet())
+        {
+            builder = builder.uriFactory(entry.getValue(), entry.getKey());
+        }
+        for (Map.Entry<String, URIFetcher> entry : blueprint.uriFetcher.getURIFetchers().entrySet())
+        {
+            builder = builder.uriFetcher(entry.getValue(), entry.getKey());
+        }
+        return builder;
     }
     
-    private JsonSchema newJsonSchema(URL schemaUrl, JsonNode schemaNode, SchemaValidatorsConfig config) {
+    private JsonSchema newJsonSchema(final URI schemaUri, final JsonNode schemaNode, final SchemaValidatorsConfig config) {
         final ValidationContext validationContext = createValidationContext(schemaNode);
         validationContext.setConfig(config);
-        JsonSchema jsonSchema = new JsonSchema(validationContext, schemaUrl, schemaNode);
+        JsonSchema jsonSchema = new JsonSchema(validationContext, schemaUri, schemaNode);
         return jsonSchema;
     }
 
-    protected ValidationContext createValidationContext(JsonNode schemaNode) {
+    protected ValidationContext createValidationContext(final JsonNode schemaNode) {
         final JsonMetaSchema jsonMetaSchema = findMetaSchemaForSchema(schemaNode);
         return new ValidationContext(jsonMetaSchema, this);
     }
 
-    private JsonMetaSchema findMetaSchemaForSchema(JsonNode schemaNode) {
+    private JsonMetaSchema findMetaSchemaForSchema(final JsonNode schemaNode) {
         final JsonNode uriNode = schemaNode.get("$schema");
         final String uri = uriNode == null || uriNode.isNull() ? defaultMetaSchemaURI: uriNode.textValue();
         final JsonMetaSchema jsonMetaSchema = jsonMetaSchemas.get(uri);
@@ -178,8 +245,12 @@ public class JsonSchemaFactory {
         }
         return jsonMetaSchema;
     }
+	
+	public URIFactory getURIFactory() {
+        return this.uriFactory;
+    }
     
-    public JsonSchema getSchema(String schema, SchemaValidatorsConfig config) {
+    public JsonSchema getSchema(final String schema, final SchemaValidatorsConfig config) {
         try {
             final JsonNode schemaNode = mapper.readTree(schema);
             return newJsonSchema(null, schemaNode, config);
@@ -189,11 +260,11 @@ public class JsonSchemaFactory {
         }
     }
 
-    public JsonSchema getSchema(String schema) {
+    public JsonSchema getSchema(final String schema) {
         return getSchema(schema, null);
     }
     
-    public JsonSchema getSchema(InputStream schemaStream, SchemaValidatorsConfig config) {
+    public JsonSchema getSchema(final InputStream schemaStream, final SchemaValidatorsConfig config) {
         try {
             final JsonNode schemaNode = mapper.readTree(schemaStream);
             return newJsonSchema(null, schemaNode, config);
@@ -203,26 +274,33 @@ public class JsonSchemaFactory {
         }
     }
 
-    public JsonSchema getSchema(InputStream schemaStream) {
+    public JsonSchema getSchema(final InputStream schemaStream) {
         return getSchema(schemaStream, null);
     }
     
-    public JsonSchema getSchema(URL schemaURL, SchemaValidatorsConfig config) {
+    public JsonSchema getSchema(final URI schemaUri, final SchemaValidatorsConfig config) {
         try {
             InputStream inputStream = null;
-            Map<String, String> map = (config != null) ? config.getUrlMappings() : new HashMap<String, String>(urlMap);
-            map.putAll(urlMap);
-            URL mappedURL = URLFactory.toURL(map.getOrDefault(schemaURL.toString(), schemaURL.toString()));
+            final Map<String, String> map = (config != null) ? config.getUriMappings() : new HashMap<String, String>(uriMap);
+            map.putAll(uriMap);
+            
+            final URI mappedUri;
+            try {
+                mappedUri = URI.create(map.getOrDefault(schemaUri.toString(), schemaUri.toString()));
+            } catch (IllegalArgumentException e) {
+                logger.error("Failed to create URI.", e);
+                throw new JsonSchemaException(e);
+            }
             try {
-                inputStream = urlFetcher.fetch(mappedURL);
-                JsonNode schemaNode = mapper.readTree(inputStream);
+                inputStream = this.uriFetcher.fetch(mappedUri);
+                final JsonNode schemaNode = mapper.readTree(inputStream);
                 final JsonMetaSchema jsonMetaSchema = findMetaSchemaForSchema(schemaNode);
 
-                if (idMatchesSourceUrl(jsonMetaSchema, schemaNode, schemaURL)) {
-                    return new JsonSchema(new ValidationContext(jsonMetaSchema, this), mappedURL, schemaNode, true /*retrieved via id, resolving will not change anything*/);
+                if (idMatchesSourceUri(jsonMetaSchema, schemaNode, schemaUri)) {
+                    return new JsonSchema(new ValidationContext(jsonMetaSchema, this), mappedUri, schemaNode, true /*retrieved via id, resolving will not change anything*/);
                 }
 
-                return newJsonSchema(mappedURL, schemaNode, config);
+                return newJsonSchema(mappedUri, schemaNode, config);
             } finally {
                 if (inputStream != null) {
                     inputStream.close();
@@ -234,36 +312,36 @@ public class JsonSchemaFactory {
         }
     }
 
-    public JsonSchema getSchema(URL schemaURL) {
-        return getSchema(schemaURL, new SchemaValidatorsConfig());
+    public JsonSchema getSchema(final URI schemaUri) {
+        return getSchema(schemaUri, new SchemaValidatorsConfig());
     }
 
-    public JsonSchema getSchema(URL schemaUrl, JsonNode jsonNode, SchemaValidatorsConfig config) {
-        return newJsonSchema(schemaUrl, jsonNode, config);
+    public JsonSchema getSchema(final URI schemaUri, final JsonNode jsonNode, final SchemaValidatorsConfig config) {
+        return newJsonSchema(schemaUri, jsonNode, config);
     }
     
 
-    public JsonSchema getSchema(JsonNode jsonNode, SchemaValidatorsConfig config) {
+    public JsonSchema getSchema(final JsonNode jsonNode, final SchemaValidatorsConfig config) {
         return newJsonSchema(null, jsonNode, config);
     }
 
-    public JsonSchema getSchema(URL schemaUrl, JsonNode jsonNode) {
-        return newJsonSchema(schemaUrl, jsonNode, null);
+    public JsonSchema getSchema(final URI schemaUri, final JsonNode jsonNode) {
+        return newJsonSchema(schemaUri, jsonNode, null);
     }
     
-    public JsonSchema getSchema(JsonNode jsonNode) {
+    public JsonSchema getSchema(final JsonNode jsonNode) {
         return newJsonSchema(null, jsonNode, null);
     }
 
-    private boolean idMatchesSourceUrl(JsonMetaSchema metaSchema, JsonNode schema, URL schemaUrl) {
+    private boolean idMatchesSourceUri(final JsonMetaSchema metaSchema, final JsonNode schema, final URI schemaUri) {
 
         String id = metaSchema.readId(schema);
         if (id == null || id.isEmpty()) {
             return false;
         }
-        boolean result = id.equals(schemaUrl.toString());
+        boolean result = id.equals(schemaUri.toString());
         if (logger.isDebugEnabled()) {
-            logger.debug("Matching " + id + " to " + schemaUrl.toString() + ": " + result);
+            logger.debug("Matching " + id + " to " + schemaUri.toString() + ": " + result);
         }
         return result;
     }
diff --git a/src/main/java/com/networknt/schema/NotValidator.java b/src/main/java/com/networknt/schema/NotValidator.java
index 564d833..b363c4f 100644
--- a/src/main/java/com/networknt/schema/NotValidator.java
+++ b/src/main/java/com/networknt/schema/NotValidator.java
@@ -30,7 +30,7 @@ public class NotValidator extends BaseJsonValidator implements JsonValidator {
 
     public NotValidator(String schemaPath, JsonNode schemaNode, JsonSchema parentSchema, ValidationContext validationContext) {
         super(schemaPath, schemaNode, parentSchema, ValidatorTypeCode.NOT, validationContext);
-        schema = new JsonSchema(validationContext, getValidatorType().getValue(), parentSchema.getCurrentUrl(), schemaNode, parentSchema);
+        schema = new JsonSchema(validationContext, getValidatorType().getValue(), parentSchema.getCurrentUri(), schemaNode, parentSchema);
 
         parseErrorCode(getValidatorType().getErrorCodeKey());
     }
diff --git a/src/main/java/com/networknt/schema/OneOfValidator.java b/src/main/java/com/networknt/schema/OneOfValidator.java
index e1905e1..76c9c8c 100644
--- a/src/main/java/com/networknt/schema/OneOfValidator.java
+++ b/src/main/java/com/networknt/schema/OneOfValidator.java
@@ -121,7 +121,7 @@ public class OneOfValidator extends BaseJsonValidator implements JsonValidator {
         int size = schemaNode.size();
         for (int i = 0; i < size; i++) {
             JsonNode childNode = schemaNode.get(i);
-            JsonSchema childSchema = new JsonSchema(validationContext, getValidatorType().getValue(), parentSchema.getCurrentUrl(), childNode, parentSchema);
+            JsonSchema childSchema = new JsonSchema(validationContext, getValidatorType().getValue(), parentSchema.getCurrentUri(), childNode, parentSchema);
             schemas.add(new ShortcutValidator(childNode, parentSchema, validationContext, childSchema));
         }
 
diff --git a/src/main/java/com/networknt/schema/PatternPropertiesValidator.java b/src/main/java/com/networknt/schema/PatternPropertiesValidator.java
index 9feb4aa..68c66d4 100644
--- a/src/main/java/com/networknt/schema/PatternPropertiesValidator.java
+++ b/src/main/java/com/networknt/schema/PatternPropertiesValidator.java
@@ -38,7 +38,7 @@ public class PatternPropertiesValidator extends BaseJsonValidator implements Jso
         Iterator<String> names = schemaNode.fieldNames();
         while (names.hasNext()) {
             String name = names.next();
-            schemas.put(Pattern.compile(name), new JsonSchema(validationContext, name, parentSchema.getCurrentUrl(), schemaNode.get(name), parentSchema));
+            schemas.put(Pattern.compile(name), new JsonSchema(validationContext, name, parentSchema.getCurrentUri(), schemaNode.get(name), parentSchema));
         }
     }
 
diff --git a/src/main/java/com/networknt/schema/PropertiesValidator.java b/src/main/java/com/networknt/schema/PropertiesValidator.java
index 3daee20..dbc61c9 100644
--- a/src/main/java/com/networknt/schema/PropertiesValidator.java
+++ b/src/main/java/com/networknt/schema/PropertiesValidator.java
@@ -32,7 +32,7 @@ public class PropertiesValidator extends BaseJsonValidator implements JsonValida
         schemas = new HashMap<String, JsonSchema>();
         for (Iterator<String> it = schemaNode.fieldNames(); it.hasNext(); ) {
             String pname = it.next();
-            schemas.put(pname, new JsonSchema(validationContext, schemaPath + "/" + pname, parentSchema.getCurrentUrl(), schemaNode.get(pname), parentSchema));
+            schemas.put(pname, new JsonSchema(validationContext, schemaPath + "/" + pname, parentSchema.getCurrentUri(), schemaNode.get(pname), parentSchema));
         }
     }
 
diff --git a/src/main/java/com/networknt/schema/RefValidator.java b/src/main/java/com/networknt/schema/RefValidator.java
index 4d1c7af..4c2ca09 100644
--- a/src/main/java/com/networknt/schema/RefValidator.java
+++ b/src/main/java/com/networknt/schema/RefValidator.java
@@ -16,8 +16,7 @@
 
 package com.networknt.schema;
 
-import java.net.MalformedURLException;
-import java.net.URL;
+import java.net.URI;
 import java.text.MessageFormat;
 import java.util.Collections;
 import java.util.Set;
@@ -26,7 +25,7 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import com.fasterxml.jackson.databind.JsonNode;
-import com.networknt.schema.url.URLFactory;
+import com.networknt.schema.uri.URIFactory;
 
 public class RefValidator extends BaseJsonValidator implements JsonValidator {
     private static final Logger logger = LoggerFactory.getLogger(RefValidator.class);
@@ -47,24 +46,24 @@ public class RefValidator extends BaseJsonValidator implements JsonValidator {
 
     static JsonSchema getRefSchema(JsonSchema parentSchema, ValidationContext validationContext, String refValue) {
         if (!refValue.startsWith(REF_CURRENT)) {
-            // This will be the url extracted from the refValue (this may be a relative or absolute Url).
-            final String refUrl;
+            // This will be the uri extracted from the refValue (this may be a relative or absolute uri).
+            final String refUri;
             final int index = refValue.indexOf(REF_CURRENT);
             if (index > 0) {
-                refUrl = refValue.substring(0, index);
+            	refUri = refValue.substring(0, index);
             } else {
-                refUrl = refValue;
+            	refUri = refValue;
             }
             
-            // This will determine the correct absolute url for the refUrl. This decision will take into
-            // account the current url of the parent schema.
-            URL schemaUrl = determineSchemaUrl(parentSchema, refUrl);
-            if (schemaUrl == null) {
+            // This will determine the correct absolute uri for the refUri. This decision will take into
+            // account the current uri of the parent schema.
+            URI schemaUri = determineSchemaUri(validationContext.getJsonSchemaFactory().getURIFactory(), parentSchema, refUri);
+            if (schemaUri == null) {
               return null;
             }
             
-            // This should retrieve schemas regardless of the protocol that is in the url.
-            parentSchema = validationContext.getJsonSchemaFactory().getSchema(schemaUrl, validationContext.getConfig());
+            // This should retrieve schemas regardless of the protocol that is in the uri.
+            parentSchema = validationContext.getJsonSchemaFactory().getSchema(schemaUri, validationContext.getConfig());
             
             if (index < 0) {
                 return parentSchema.findAncestor();
@@ -77,28 +76,29 @@ public class RefValidator extends BaseJsonValidator implements JsonValidator {
         } else {
             JsonNode node = parentSchema.getRefSchemaNode(refValue);
             if (node != null) {
-                return new JsonSchema(validationContext, refValue, parentSchema.getCurrentUrl(), node, parentSchema);
+                return new JsonSchema(validationContext, refValue, parentSchema.getCurrentUri(), node, parentSchema);
             }
         }
         return null;
     }
 
-    private static URL determineSchemaUrl(JsonSchema parentSchema, String refUrl) {
-        URL schemaUrl;
-        try {
-            // If the refUrl is an absolute url, then this will succeed.
-            schemaUrl = URLFactory.toURL(refUrl);
-        } catch (MalformedURLException e) {
-            try {
-                // If the refUrl is a valid relative url in the context of the parent schema's url,
-                // then this will succeed.
-                schemaUrl = URLFactory.toURL(parentSchema.getCurrentUrl(), refUrl);
-            } catch (MalformedURLException e2) {
-                // We are unable to resolve the reference at this point.
-                schemaUrl = null;
+    private static URI determineSchemaUri(final URIFactory uriFactory, final JsonSchema parentSchema, final String refUri) {
+    	URI schemaUri;
+    	final URI currentUri = parentSchema.getCurrentUri();
+    	try
+    	{
+            if (currentUri == null)
+            {
+    	        schemaUri = uriFactory.create(refUri);
             }
-        }
-        return schemaUrl;
+            else
+            {
+                schemaUri = uriFactory.create(currentUri, refUri);
+            }
+    	} catch (IllegalArgumentException e) {
+    	    schemaUri = null;
+    	}
+        return schemaUri;
     }
 
     public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String at) {
diff --git a/src/main/java/com/networknt/schema/SchemaValidatorsConfig.java b/src/main/java/com/networknt/schema/SchemaValidatorsConfig.java
index cec3135..15bdc66 100644
--- a/src/main/java/com/networknt/schema/SchemaValidatorsConfig.java
+++ b/src/main/java/com/networknt/schema/SchemaValidatorsConfig.java
@@ -16,7 +16,6 @@
 
 package com.networknt.schema;
 
-import java.net.URL;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -43,7 +42,7 @@ public class SchemaValidatorsConfig {
      * validation of schemas that refer to public URLs. This is merged with any mappings the {@link JsonSchemaFactory} 
      * may have been built with.
      */
-    private Map<String, String> urlMappings = new HashMap<String, String>();
+    private Map<String, String> uriMappings = new HashMap<String, String>();
 
     public boolean isTypeLoose() {
         return typeLoose;
@@ -53,13 +52,13 @@ public class SchemaValidatorsConfig {
         this.typeLoose = typeLoose;
     }
 
-    public Map<String, String> getUrlMappings() {
+    public Map<String, String> getUriMappings() {
         // return a copy of the mappings
-        return new HashMap<String, String>(urlMappings);
+        return new HashMap<String, String>(uriMappings);
     }
 
-    public void setUrlMappings(Map<String, String> urlMappings) {
-        this.urlMappings = urlMappings;
+    public void setUriMappings(Map<String, String> uriMappings) {
+        this.uriMappings = uriMappings;
     }
     
     public boolean isMissingNodeAsError() {
@@ -84,6 +83,6 @@ public class SchemaValidatorsConfig {
 
     private void loadDefaultConfig() {
         this.typeLoose = true;
-        this.urlMappings = new HashMap<String, String>();
+        this.uriMappings = new HashMap<String, String>();
     }
 }
diff --git a/src/main/java/com/networknt/schema/UUIDValidator.java b/src/main/java/com/networknt/schema/UUIDValidator.java
index 49fda9f..4c4175c 100644
--- a/src/main/java/com/networknt/schema/UUIDValidator.java
+++ b/src/main/java/com/networknt/schema/UUIDValidator.java
@@ -16,17 +16,14 @@
 
 package com.networknt.schema;
 
-import com.fasterxml.jackson.databind.JsonNode;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.text.ParsePosition;
-import java.text.SimpleDateFormat;
 import java.util.Collections;
 import java.util.LinkedHashSet;
 import java.util.Set;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.fasterxml.jackson.databind.JsonNode;
 
 public class UUIDValidator extends BaseJsonValidator implements JsonValidator {
     private static final Logger logger = LoggerFactory.getLogger(UUIDValidator.class);
diff --git a/src/main/java/com/networknt/schema/UnionTypeValidator.java b/src/main/java/com/networknt/schema/UnionTypeValidator.java
index 5d6d977..19bf7af 100644
--- a/src/main/java/com/networknt/schema/UnionTypeValidator.java
+++ b/src/main/java/com/networknt/schema/UnionTypeValidator.java
@@ -50,7 +50,7 @@ public class UnionTypeValidator extends BaseJsonValidator implements JsonValidat
             sep = ", ";
 
             if (n.isObject())
-                schemas.add(new JsonSchema(validationContext, ValidatorTypeCode.TYPE.getValue(), parentSchema.getCurrentUrl(), n, parentSchema));
+                schemas.add(new JsonSchema(validationContext, ValidatorTypeCode.TYPE.getValue(), parentSchema.getCurrentUri(), n, parentSchema));
             else
                 schemas.add(new TypeValidator(schemaPath + "/" + i, n, parentSchema, validationContext));
 
diff --git a/src/main/java/com/networknt/schema/ValidatorTypeCode.java b/src/main/java/com/networknt/schema/ValidatorTypeCode.java
index 63655a7..18e7e8e 100644
--- a/src/main/java/com/networknt/schema/ValidatorTypeCode.java
+++ b/src/main/java/com/networknt/schema/ValidatorTypeCode.java
@@ -64,7 +64,8 @@ public enum ValidatorTypeCode implements Keyword, ErrorMessageType {
     UNION_TYPE("unionType", "1030", new MessageFormat("{0}: {1} found, but {2} is required")),
     UNIQUE_ITEMS("uniqueItems", "1031", new MessageFormat("{0}: the items in the array must be unique")),
     DATETIME("date-time", "1034", new MessageFormat("{0}: {1} is an invalid {2}")),
-    UUID("uuid", "1035", new MessageFormat("{0}: {1} is an invalid {2}"));
+    UUID("uuid", "1035", new MessageFormat("{0}: {1} is an invalid {2}")),
+    ID("id", "1036", new MessageFormat("{0}: {1} is an invalid segment for URI {2}"));
 
 	private static Map<String, ValidatorTypeCode> constants = new HashMap<String, ValidatorTypeCode>();
 
diff --git a/src/main/java/com/networknt/schema/uri/ClasspathURLFactory.java b/src/main/java/com/networknt/schema/uri/ClasspathURLFactory.java
new file mode 100644
index 0000000..62ec82f
--- /dev/null
+++ b/src/main/java/com/networknt/schema/uri/ClasspathURLFactory.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2016 Network New Technologies Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.networknt.schema.uri;
+
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.net.URLStreamHandler;
+import java.util.Collections;
+import java.util.Set;
+
+/**
+ * A URIFactory that uses {@link #URL(String)} and {@link #URL(URL, String) for creating {@link URI}s.
+ */
+public final class ClasspathURLFactory implements URIFactory {
+  static final URLStreamHandler STREAM_HANDLER = new ClasspathURLStreamHandler();
+  
+  public static final Set<String> SUPPORTED_SCHEMES = Collections.unmodifiableSet(
+    ClasspathURLStreamHandler.SUPPORTED_SCHEMES);
+  
+  public static URL convert(final URI uri) throws MalformedURLException {
+    return new URL(null, uri.toString(), STREAM_HANDLER);
+  }
+  
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public URI create(final String uri)
+  {
+    try {
+      return new URL(null, uri, STREAM_HANDLER).toURI();
+    } catch (MalformedURLException | URISyntaxException e) {
+      throw new IllegalArgumentException("Unable to create URI.", e);
+    }
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public URI create(final URI baseURI, final String segment)
+  {
+    try {
+      return new URL(convert(baseURI), segment, STREAM_HANDLER).toURI();
+    } catch (MalformedURLException | URISyntaxException e) {
+      throw new IllegalArgumentException("Unable to create URI.", e);
+    }
+  }
+}
diff --git a/src/main/java/com/networknt/schema/url/StandardURLFetcher.java b/src/main/java/com/networknt/schema/uri/ClasspathURLFetcher.java
similarity index 51%
rename from src/main/java/com/networknt/schema/url/StandardURLFetcher.java
rename to src/main/java/com/networknt/schema/uri/ClasspathURLFetcher.java
index 170f4fb..5774510 100644
--- a/src/main/java/com/networknt/schema/url/StandardURLFetcher.java
+++ b/src/main/java/com/networknt/schema/uri/ClasspathURLFetcher.java
@@ -14,19 +14,28 @@
  * limitations under the License.
  */
 
-package com.networknt.schema.url;
+package com.networknt.schema.uri;
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.net.URI;
 import java.net.URL;
+import java.util.Collections;
+import java.util.Set;
 
 /**
- * Standard URL fetcher that uses {@link URL#openStream()} for fetching.
+ * A URIfetcher that uses {@link URL#openStream()} for fetching and assumes given {@link URI}s
+ * are actualy {@link URL}s.
  */
-public class StandardURLFetcher implements URLFetcher {
-
-    @Override
-    public InputStream fetch(URL url) throws IOException {
-        return url.openStream();
-    }
+public final class ClasspathURLFetcher implements URIFetcher {
+  // This fetcher handles the {@link URL}s created with the {@link ClasspathURIFactory}.
+  public static final Set<String> SUPPORTED_SCHEMES = Collections.unmodifiableSet(ClasspathURLFactory.SUPPORTED_SCHEMES);
+  
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public InputStream fetch(final URI uri) throws IOException {
+    return ClasspathURLFactory.convert(uri).openStream();
+  }
 }
diff --git a/src/main/java/com/networknt/schema/url/ClasspathURLStreamHandler.java b/src/main/java/com/networknt/schema/uri/ClasspathURLStreamHandler.java
similarity index 90%
rename from src/main/java/com/networknt/schema/url/ClasspathURLStreamHandler.java
rename to src/main/java/com/networknt/schema/uri/ClasspathURLStreamHandler.java
index ab08c9b..7ccc68c 100644
--- a/src/main/java/com/networknt/schema/url/ClasspathURLStreamHandler.java
+++ b/src/main/java/com/networknt/schema/uri/ClasspathURLStreamHandler.java
@@ -14,13 +14,17 @@
  * limitations under the License.
  */
 
-package com.networknt.schema.url;
+package com.networknt.schema.uri;
 
 import java.io.IOException;
 import java.io.InputStream;
 import java.net.URL;
 import java.net.URLConnection;
 import java.net.URLStreamHandler;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
 
 /**
  * An {@link URLStreamHandler} capable of loading resources from the classpath.
@@ -28,12 +32,8 @@ import java.net.URLStreamHandler;
  * @author <a href="mailto:kenneth.waldenstroem@gmail.com">Kenneth Waldenstrom</a>
  */
 class ClasspathURLStreamHandler extends URLStreamHandler {
-  private final static String CLASSPATH_PREFIX = "classpath:";
-  private final static String RESOURCE_PREFIX = "resource:";
-
-  boolean supports(final String pURL) {
-    return pURL.startsWith(CLASSPATH_PREFIX) || pURL.startsWith(RESOURCE_PREFIX);
-  }
+  public static final Set<String> SUPPORTED_SCHEMES = Collections.unmodifiableSet(new HashSet<>(
+    Arrays.asList("classpath", "resource")));
 
   @Override
   protected URLConnection openConnection(final URL pURL) throws IOException {
@@ -98,4 +98,4 @@ class ClasspathURLStreamHandler extends URLStreamHandler {
   }
 
 
-}
+}
\ No newline at end of file
diff --git a/src/main/java/com/networknt/schema/uri/URIFactory.java b/src/main/java/com/networknt/schema/uri/URIFactory.java
new file mode 100644
index 0000000..56d6a24
--- /dev/null
+++ b/src/main/java/com/networknt/schema/uri/URIFactory.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2016 Network New Technologies Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.networknt.schema.uri;
+
+import java.net.URI;
+
+/**
+ * The URIFactory interface defines how {@link URI}s are able to be combined and created.
+ */
+public interface URIFactory {
+  /**
+   * @param uri Some uri string.
+   * @return The converted {@link URI}.
+   * @throws IllegalArgumentException if there was a problem creating the {@link URI} with the given data.
+   */
+  URI create(String uri);
+  
+  /**
+   * @param baseURI The base {@link URI}.
+   * @param segment The segment to add to the base {@link URI}. 
+   * @return The combined {@link URI}.
+   * @throws IllegalArgumentException if there was a problem creating the {@link URI} with the given data.
+   */
+  URI create(URI baseURI, String segment);
+}
diff --git a/src/main/java/com/networknt/schema/url/URLFetcher.java b/src/main/java/com/networknt/schema/uri/URIFetcher.java
similarity index 73%
rename from src/main/java/com/networknt/schema/url/URLFetcher.java
rename to src/main/java/com/networknt/schema/uri/URIFetcher.java
index 5160fa2..35e32a8 100644
--- a/src/main/java/com/networknt/schema/url/URLFetcher.java
+++ b/src/main/java/com/networknt/schema/uri/URIFetcher.java
@@ -14,12 +14,15 @@
  * limitations under the License.
  */
 
-package com.networknt.schema.url;
+package com.networknt.schema.uri;
 
 import java.io.IOException;
 import java.io.InputStream;
-import java.net.URL;
+import java.net.URI;
 
-public interface URLFetcher {
-    InputStream fetch(URL url) throws IOException;
+/**
+ * The URIFetcher interface defines how file streams are able to be fetched given a {@link URI}.
+ */
+public interface URIFetcher {
+  InputStream fetch(URI uri) throws IOException;
 }
diff --git a/src/main/java/com/networknt/schema/uri/URISchemeFactory.java b/src/main/java/com/networknt/schema/uri/URISchemeFactory.java
new file mode 100644
index 0000000..cf58a41
--- /dev/null
+++ b/src/main/java/com/networknt/schema/uri/URISchemeFactory.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2016 Network New Technologies Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.networknt.schema.uri;
+
+import java.net.URI;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * The URISchemaFactory will proxy to other {@link URIFactory}s based on the scheme being used.
+ */
+public class URISchemeFactory implements URIFactory{
+  private static final Pattern URI_SCHEME_PATTERN = Pattern.compile("^([a-z][a-z0-9+\\.\\-\\\\]*):");
+  
+  private final Map<String, URIFactory> uriFactories;
+  
+  public URISchemeFactory(final Map<String, URIFactory> uriFactories) {
+    if (uriFactories == null) {
+      throw new IllegalArgumentException("URIFactory map must not be null");
+    }
+    this.uriFactories = uriFactories;
+  }
+  
+  public Map<String, URIFactory> getURIFactories() {
+    return this.uriFactories;
+  }
+  
+  private String getScheme(final String uri) {
+    final Matcher matcher = URI_SCHEME_PATTERN.matcher(uri);
+    if (matcher.find()) {
+      return matcher.group(1);
+    } else {
+      return null;
+    }
+  }
+  
+  private URIFactory getFactory(final String scheme) {
+    final URIFactory uriFactory = this.uriFactories.get(scheme);
+    if (uriFactory == null) {
+      throw new IllegalArgumentException(String.format("Unsupported URI scheme encountered: %s", scheme));
+    }
+    return uriFactory;
+  }
+  
+  /**
+   * {@inheritdoc}
+   */
+  public URI create(final String uri) {
+    final String scheme = this.getScheme(uri);
+    if (scheme == null) {
+      throw new IllegalArgumentException(String.format("Couldn't find URI scheme: %s", uri));
+    }
+    
+    final URIFactory uriFactory = this.getFactory(scheme);
+    return uriFactory.create(uri);
+  }
+  
+  /**
+   * {@inheritdoc}
+   */
+  public URI create(final URI baseURI, final String segment) {
+    if (baseURI == null) {
+      return this.create(segment);
+    }
+    else {
+      // We first attempt to get the scheme in case the segment is an absolute URI path.
+      String scheme = this.getScheme(segment);
+      if (scheme == null) {
+        // In this case, the segment is relative to the baseURI.
+        scheme = baseURI.getScheme();
+        final URIFactory uriFactory = this.getFactory(scheme);
+        return uriFactory.create(baseURI, segment);
+      } else {
+        // In this case, the segment is an absolute URI path.
+        final URIFactory uriFactory = this.getFactory(scheme);
+        return uriFactory.create(segment);
+      }
+    }
+  }
+}
diff --git a/src/main/java/com/networknt/schema/uri/URISchemeFetcher.java b/src/main/java/com/networknt/schema/uri/URISchemeFetcher.java
new file mode 100644
index 0000000..a390c0f
--- /dev/null
+++ b/src/main/java/com/networknt/schema/uri/URISchemeFetcher.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2016 Network New Technologies Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.networknt.schema.uri;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.util.Map;
+
+/**
+ * The URISchemeFetcher will proxy to other {@link URIFetcher}s based on the scheme being used.
+ */
+public class URISchemeFetcher implements URIFetcher {
+  private final Map<String, URIFetcher> uriFetchers;
+  
+  public URISchemeFetcher(final Map<String, URIFetcher> uriFetchers) {
+    if (uriFetchers == null) {
+      throw new IllegalArgumentException("URIFetcher map must not be null");
+    }
+    this.uriFetchers = uriFetchers;
+  }
+  
+  public Map<String, URIFetcher> getURIFetchers() {
+    return this.uriFetchers;
+  }
+  
+  /**
+   * {@inheritdoc}
+   */
+  public InputStream fetch(final URI uri) throws IOException {
+    final URIFetcher uriFetcher = this.uriFetchers.get(uri.getScheme());
+	if (uriFetcher == null) {
+      throw new IllegalArgumentException(String.format("Unsupported URI scheme encountered: %s", uri.getScheme()));
+	}
+    return uriFetcher.fetch(uri);
+  }
+}
diff --git a/src/main/java/com/networknt/schema/uri/URLFactory.java b/src/main/java/com/networknt/schema/uri/URLFactory.java
new file mode 100644
index 0000000..6ed5f0d
--- /dev/null
+++ b/src/main/java/com/networknt/schema/uri/URLFactory.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2016 Network New Technologies Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.networknt.schema.uri;
+
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * A URIFactory that uses {@link #URL(String)} and {@link #URL(URL, String) for creating {@link URI}s.
+ */
+public final class URLFactory implements URIFactory {
+  // These supported schemes are defined in {@link #URL(String, String, int, String)}.
+  public static final Set<String> SUPPORTED_SCHEMES = Collections.unmodifiableSet(new HashSet<>(
+    Arrays.asList("http", "https", "ftp", "file", "jar")));
+  
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public URI create(final String uri)
+  {
+    try {
+      return new URL(uri).toURI();
+    } catch (MalformedURLException | URISyntaxException e) {
+      throw new IllegalArgumentException("Unable to create URI.", e);
+    }
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public URI create(final URI baseURI, final String segment)
+  {
+    try {
+      return new URL(baseURI.toURL(), segment).toURI();
+    } catch (MalformedURLException | URISyntaxException e) {
+      throw new IllegalArgumentException("Unable to create URI.", e);
+    }
+  }
+}
diff --git a/src/main/java/com/networknt/schema/uri/URLFetcher.java b/src/main/java/com/networknt/schema/uri/URLFetcher.java
new file mode 100644
index 0000000..27696bf
--- /dev/null
+++ b/src/main/java/com/networknt/schema/uri/URLFetcher.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2016 Network New Technologies Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.networknt.schema.uri;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.net.URL;
+import java.util.Collections;
+import java.util.Set;
+
+/**
+ * A URIfetcher that uses {@link URL#openStream()} for fetching and assumes given {@link URI}s
+ * are actualy {@link URL}s.
+ */
+public final class URLFetcher implements URIFetcher {
+  // These supported schemes are defined in {@link #URL(String, String, int, String)}.
+  // This fetcher also supports the {@link URL}s created with the {@link ClasspathURIFactory}.
+  public static final Set<String> SUPPORTED_SCHEMES = Collections.unmodifiableSet(URLFactory.SUPPORTED_SCHEMES);
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public InputStream fetch(final URI uri) throws IOException {
+    return uri.toURL().openStream();
+  }
+}
diff --git a/src/main/java/com/networknt/schema/url/URLFactory.java b/src/main/java/com/networknt/schema/url/URLFactory.java
deleted file mode 100644
index 430980c..0000000
--- a/src/main/java/com/networknt/schema/url/URLFactory.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (c) 2016 Network New Technologies Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.networknt.schema.url;
-
-import java.net.MalformedURLException;
-import java.net.URL;
-
-/**
- * A factory for creating {@link URL}'s. This factory creates {@link URL}'s that in additional to the standard {@link URL}'s
- * capability of loading resources using http, https, file, etc. also makes it possible to load resources from
- * the applications classpath. To load a resource from classpath, the url must be prefixed either with <i>classpath:</i>
- * or <i>resource:</i>
- *
- * To ensure that we support classpath resources, this class should be used instead of <code>new URL(pURL)</code>
- *
- * @author <a href="mailto:kenneth.waldenstroem@gmail.com">Kenneth Waldenstrom</a>
- */
-public class URLFactory {
-  private static final ClasspathURLStreamHandler sClasspathURLStreamHandler = new ClasspathURLStreamHandler();
-
-  /**
-   * Creates an {@link URL} based on the provided string and parent url.
-   * @param parentUrl the parent URL of the given url (if no parent exists, then this can be null).
-   * @param url the url
-   * @return a {@link URL}
-   * @throws MalformedURLException if the url is not a proper URL
-   */
-  public static URL toURL(final URL parentUrl, final String url) throws MalformedURLException {
-    return new URL(parentUrl, url, sClasspathURLStreamHandler.supports(url) ? sClasspathURLStreamHandler : null);
-  }
-  
-  /**
-   * Creates an {@link URL} based on the provided string
-   * @param url the url
-   * @return a {@link URL}
-   * @throws MalformedURLException if the url is not a proper URL
-   */
-  public static URL toURL(final String url) throws MalformedURLException {
-    return new URL(null, url, sClasspathURLStreamHandler.supports(url) ? sClasspathURLStreamHandler : null);
-  }
-}
\ No newline at end of file


Test patch:
diff --git a/src/test/java/com/networknt/schema/BaseJsonSchemaValidatorTest.java b/src/test/java/com/networknt/schema/BaseJsonSchemaValidatorTest.java
index 06efc8a..aa3d9b2 100644
--- a/src/test/java/com/networknt/schema/BaseJsonSchemaValidatorTest.java
+++ b/src/test/java/com/networknt/schema/BaseJsonSchemaValidatorTest.java
@@ -16,12 +16,13 @@
 
 package com.networknt.schema;
 
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.ObjectMapper;
-
 import java.io.InputStream;
+import java.net.URI;
 import java.net.URL;
 
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
 /**
  * Created by steve on 22/10/16.
  */
@@ -62,9 +63,9 @@ public class BaseJsonSchemaValidatorTest {
         return schema;
     }
 
-    protected JsonSchema getJsonSchemaFromUrl(String url) throws Exception {
+    protected JsonSchema getJsonSchemaFromUrl(String uri) throws Exception {
         JsonSchemaFactory factory = JsonSchemaFactory.getInstance();
-        JsonSchema schema = factory.getSchema(new URL(url));
+        JsonSchema schema = factory.getSchema(new URI(uri));
         return schema;
     }
 
diff --git a/src/test/java/com/networknt/schema/JsonSchemaTest.java b/src/test/java/com/networknt/schema/JsonSchemaTest.java
index 4600393..3b19162 100644
--- a/src/test/java/com/networknt/schema/JsonSchemaTest.java
+++ b/src/test/java/com/networknt/schema/JsonSchemaTest.java
@@ -20,6 +20,7 @@ import static io.undertow.Handlers.resource;
 
 import java.io.File;
 import java.io.InputStream;
+import java.net.URI;
 import java.net.URL;
 import java.util.ArrayList;
 import java.util.List;
@@ -32,7 +33,6 @@ import org.junit.Test;
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.node.ArrayNode;
-import com.networknt.schema.url.URLFactory;
 
 import io.undertow.Undertow;
 import io.undertow.server.handlers.resource.FileResourceManager;
@@ -70,7 +70,7 @@ public class JsonSchemaTest {
     }
 
     private void runTestFile(String testCaseFile) throws Exception {
-        final URL testCaseFileUrl = URLFactory.toURL("classpath:" + testCaseFile);
+        final URI testCaseFileUri = URI.create("classpath:" + testCaseFile);
         InputStream in = Thread.currentThread().getContextClassLoader()
                 .getResourceAsStream(testCaseFile);
         ArrayNode testCases = (ArrayNode) mapper.readTree(in);
@@ -88,7 +88,7 @@ public class JsonSchemaTest {
                     // Configure the schemaValidator to set typeLoose's value based on the test file,
                     // if test file do not contains typeLoose flag, use default value: true.
                     config.setTypeLoose((typeLooseNode == null) ? false : typeLooseNode.asBoolean());
-                    JsonSchema schema = validatorFactory.getSchema(testCaseFileUrl, testCase.get("schema"), config);
+                    JsonSchema schema = validatorFactory.getSchema(testCaseFileUri, testCase.get("schema"), config);
                     List<ValidationMessage> errors = new ArrayList<ValidationMessage>();
 
                     errors.addAll(schema.validate(node));
diff --git a/src/test/java/com/networknt/schema/UrlMappingTest.java b/src/test/java/com/networknt/schema/UriMappingTest.java
similarity index 68%
rename from src/test/java/com/networknt/schema/UrlMappingTest.java
rename to src/test/java/com/networknt/schema/UriMappingTest.java
index 72bf01b..f0f95a5 100644
--- a/src/test/java/com/networknt/schema/UrlMappingTest.java
+++ b/src/test/java/com/networknt/schema/UriMappingTest.java
@@ -1,49 +1,54 @@
 package com.networknt.schema;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.net.MalformedURLException;
+import java.net.URI;
 import java.net.URL;
 import java.net.UnknownHostException;
 import java.util.HashMap;
 import java.util.Map;
 
+import org.junit.Test;
+
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.networknt.schema.JsonSchemaFactory.Builder;
-import com.networknt.schema.url.URLFactory;
+import com.networknt.schema.uri.ClasspathURLFactory;
+import com.networknt.schema.uri.URLFactory;
 
-import org.junit.Test;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.fail;
-
-public class UrlMappingTest {
+public class UriMappingTest {
 
     private final ObjectMapper mapper = new ObjectMapper();
+    private final ClasspathURLFactory classpathURLFactory = new ClasspathURLFactory();
+    private final URLFactory urlFactory = new URLFactory();
 
     /**
-     * Validate that a JSON URL Mapping file containing the URL Mapping schema is
+     * Validate that a JSON URI Mapping file containing the URI Mapping schema is
      * schema valid.
      * 
      * @throws IOException if unable to parse the mapping file
      */
     @Test
-    public void testBuilderUrlMappingUrl() throws IOException {
-        URL mappings = URLFactory.toURL("resource:tests/url_mapping/url-mapping.json");
+    public void testBuilderUriMappingUri() throws IOException {
+        URL mappings = ClasspathURLFactory.convert(
+                this.classpathURLFactory.create("resource:tests/uri_mapping/uri-mapping.json"));
         JsonMetaSchema draftV4 = JsonMetaSchema.getDraftV4();
         Builder builder = JsonSchemaFactory.builder()
                 .defaultMetaSchemaURI(draftV4.getUri())
                 .addMetaSchema(draftV4)
-                .addUrlMappings(getUrlMappingsFromUrl(mappings));
+                .addUriMappings(getUriMappingsFromUrl(mappings));
         JsonSchemaFactory instance = builder.build();
-        JsonSchema schema = instance.getSchema(new URL(
-                "https://raw.githubusercontent.com/networknt/json-schema-validator/master/src/test/resources/tests/url_mapping/url-mapping.schema.json"));
+        JsonSchema schema = instance.getSchema(this.urlFactory.create(
+                "https://raw.githubusercontent.com/networknt/json-schema-validator/master/src/test/resources/tests/uri_mapping/uri-mapping.schema.json"));
         assertEquals(0, schema.validate(mapper.readTree(mappings)).size());
     }
 
     /**
-     * Validate that local URL is used when attempting to get a schema that is not
+     * Validate that local URI is used when attempting to get a schema that is not
      * available publicly. Use the URL http://example.com/invalid/schema/url to use
      * a public URL that returns a 404 Not Found. The locally mapped schema is a
      * valid, but empty schema.
@@ -53,7 +58,7 @@ public class UrlMappingTest {
     @Test
     public void testBuilderExampleMappings() throws IOException {
         JsonSchemaFactory instance = JsonSchemaFactory.getInstance();
-        URL example = new URL("http://example.com/invalid/schema/url");
+        URI example = this.urlFactory.create("http://example.com/invalid/schema/url");
         // first test that attempting to use example URL throws an error
         try {
             JsonSchema schema = instance.getSchema(example);
@@ -68,31 +73,33 @@ public class UrlMappingTest {
         } catch (Exception ex) {
             fail("Unexpected exception thrown");
         }
-        URL mappings = URLFactory.toURL("resource:tests/url_mapping/invalid-schema-url.json");
+        URL mappings = ClasspathURLFactory.convert(
+          this.classpathURLFactory.create("resource:tests/uri_mapping/invalid-schema-uri.json"));
         JsonMetaSchema draftV4 = JsonMetaSchema.getDraftV4();
         Builder builder = JsonSchemaFactory.builder()
                 .defaultMetaSchemaURI(draftV4.getUri())
                 .addMetaSchema(draftV4)
-                .addUrlMappings(getUrlMappingsFromUrl(mappings));
+                .addUriMappings(getUriMappingsFromUrl(mappings));
         instance = builder.build();
         JsonSchema schema = instance.getSchema(example);
         assertEquals(0, schema.validate(mapper.createObjectNode()).size());
     }
 
     /**
-     * Validate that a JSON URL Mapping file containing the URL Mapping schema is
+     * Validate that a JSON URI Mapping file containing the URI Mapping schema is
      * schema valid.
      * 
      * @throws IOException if unable to parse the mapping file
      */
     @Test
-    public void testValidatorConfigUrlMappingUrl() throws IOException {
+    public void testValidatorConfigUriMappingUri() throws IOException {
         JsonSchemaFactory instance = JsonSchemaFactory.getInstance();
-        URL mappings = URLFactory.toURL("resource:tests/url_mapping/url-mapping.json");
+        URL mappings = ClasspathURLFactory.convert(
+                this.classpathURLFactory.create("resource:tests/uri_mapping/uri-mapping.json"));
         SchemaValidatorsConfig config = new SchemaValidatorsConfig();
-        config.setUrlMappings(getUrlMappingsFromUrl(mappings));
-        JsonSchema schema = instance.getSchema(new URL(
-                "https://raw.githubusercontent.com/networknt/json-schema-validator/master/src/test/resources/tests/url_mapping/url-mapping.schema.json"),
+        config.setUriMappings(getUriMappingsFromUrl(mappings));
+        JsonSchema schema = instance.getSchema(this.urlFactory.create(
+                "https://raw.githubusercontent.com/networknt/json-schema-validator/master/src/test/resources/tests/uri_mapping/uri-mapping.schema.json"),
                 config);
         assertEquals(0, schema.validate(mapper.readTree(mappings)).size());
     }
@@ -109,7 +116,7 @@ public class UrlMappingTest {
     public void testValidatorConfigExampleMappings() throws IOException {
         JsonSchemaFactory instance = JsonSchemaFactory.getInstance();
         SchemaValidatorsConfig config = new SchemaValidatorsConfig();
-        URL example = new URL("http://example.com/invalid/schema/url");
+        URI example = this.urlFactory.create("http://example.com/invalid/schema/url");
         // first test that attempting to use example URL throws an error
         try {
             JsonSchema schema = instance.getSchema(example, config);
@@ -124,8 +131,9 @@ public class UrlMappingTest {
         } catch (Exception ex) {
             fail("Unexpected exception thrown");
         }
-        URL mappings = URLFactory.toURL("resource:tests/url_mapping/invalid-schema-url.json");
-        config.setUrlMappings(getUrlMappingsFromUrl(mappings));
+        URL mappings = ClasspathURLFactory.convert(
+                this.classpathURLFactory.create("resource:tests/uri_mapping/invalid-schema-uri.json"));
+        config.setUriMappings(getUriMappingsFromUrl(mappings));
         JsonSchema schema = instance.getSchema(example, config);
         assertEquals(0, schema.validate(mapper.createObjectNode()).size());
     }
@@ -133,15 +141,16 @@ public class UrlMappingTest {
     @Test
     public void testMappingsForRef() throws IOException {
         JsonSchemaFactory instance = JsonSchemaFactory.getInstance();
-        URL mappings = URLFactory.toURL("resource:tests/url_mapping/schema-with-ref-mapping.json");
+        URL mappings = ClasspathURLFactory.convert(
+                this.classpathURLFactory.create("resource:tests/uri_mapping/schema-with-ref-mapping.json"));
         SchemaValidatorsConfig config = new SchemaValidatorsConfig();
-        config.setUrlMappings(getUrlMappingsFromUrl(mappings));
-        JsonSchema schema = instance.getSchema(URLFactory.toURL("resource:tests/url_mapping/schema-with-ref.json"),
+        config.setUriMappings(getUriMappingsFromUrl(mappings));
+        JsonSchema schema = instance.getSchema(this.classpathURLFactory.create("resource:tests/uri_mapping/schema-with-ref.json"),
                 config);
         assertEquals(0, schema.validate(mapper.readTree("[]")).size());
     }
 
-    private Map<String, String> getUrlMappingsFromUrl(URL url) throws MalformedURLException, IOException {
+    private Map<String, String> getUriMappingsFromUrl(URL url) throws MalformedURLException, IOException {
         HashMap<String, String> map = new HashMap<String, String>();
         for (JsonNode mapping : mapper.readTree(url)) {
             map.put(mapping.get("publicURL").asText(),
diff --git a/src/test/resources/tests/url_mapping/example-schema.json b/src/test/resources/tests/uri_mapping/example-schema.json
similarity index 100%
rename from src/test/resources/tests/url_mapping/example-schema.json
rename to src/test/resources/tests/uri_mapping/example-schema.json
diff --git a/src/test/resources/tests/url_mapping/invalid-schema-url.json b/src/test/resources/tests/uri_mapping/invalid-schema-uri.json
similarity index 76%
rename from src/test/resources/tests/url_mapping/invalid-schema-url.json
rename to src/test/resources/tests/uri_mapping/invalid-schema-uri.json
index 8793df9..5ccc1e6 100644
--- a/src/test/resources/tests/url_mapping/invalid-schema-url.json
+++ b/src/test/resources/tests/uri_mapping/invalid-schema-uri.json
@@ -5,6 +5,6 @@
     },
     {
         "publicURL": "http://example.com/invalid/schema/url",
-        "localURL": "resource:/tests/url_mapping/example-schema.json"
+        "localURL": "resource:/tests/uri_mapping/example-schema.json"
     }
 ]
diff --git a/src/test/resources/tests/url_mapping/schema-with-ref-mapping.json b/src/test/resources/tests/uri_mapping/schema-with-ref-mapping.json
similarity index 76%
rename from src/test/resources/tests/url_mapping/schema-with-ref-mapping.json
rename to src/test/resources/tests/uri_mapping/schema-with-ref-mapping.json
index 8793df9..5ccc1e6 100644
--- a/src/test/resources/tests/url_mapping/schema-with-ref-mapping.json
+++ b/src/test/resources/tests/uri_mapping/schema-with-ref-mapping.json
@@ -5,6 +5,6 @@
     },
     {
         "publicURL": "http://example.com/invalid/schema/url",
-        "localURL": "resource:/tests/url_mapping/example-schema.json"
+        "localURL": "resource:/tests/uri_mapping/example-schema.json"
     }
 ]
diff --git a/src/test/resources/tests/url_mapping/schema-with-ref.json b/src/test/resources/tests/uri_mapping/schema-with-ref.json
similarity index 100%
rename from src/test/resources/tests/url_mapping/schema-with-ref.json
rename to src/test/resources/tests/uri_mapping/schema-with-ref.json
diff --git a/src/test/resources/tests/url_mapping/url-mapping.json b/src/test/resources/tests/uri_mapping/uri-mapping.json
similarity index 58%
rename from src/test/resources/tests/url_mapping/url-mapping.json
rename to src/test/resources/tests/uri_mapping/uri-mapping.json
index efd7e6b..a66b7bd 100644
--- a/src/test/resources/tests/url_mapping/url-mapping.json
+++ b/src/test/resources/tests/uri_mapping/uri-mapping.json
@@ -4,7 +4,7 @@
         "localURL": "resource:/draftv4.schema.json" 
     },
     {
-        "publicURL": "https://raw.githubusercontent.com/networknt/json-schema-validator/master/src/test/resources/tests/url_mapping/url-mapping.schema.json",
-        "localURL": "resource:/tests/url_mapping/url-mapping.schema.json"
+        "publicURL": "https://raw.githubusercontent.com/networknt/json-schema-validator/master/src/test/resources/tests/uri_mapping/uri-mapping.schema.json",
+        "localURL": "resource:/tests/uri_mapping/uri-mapping.schema.json"
     }
 ]
diff --git a/src/test/resources/tests/url_mapping/url-mapping.schema.json b/src/test/resources/tests/uri_mapping/uri-mapping.schema.json
similarity index 93%
rename from src/test/resources/tests/url_mapping/url-mapping.schema.json
rename to src/test/resources/tests/uri_mapping/uri-mapping.schema.json
index 5d43cd9..beb1708 100644
--- a/src/test/resources/tests/url_mapping/url-mapping.schema.json
+++ b/src/test/resources/tests/uri_mapping/uri-mapping.schema.json
@@ -1,6 +1,6 @@
 {
     "$schema": "http://json-schema.org/draft-04/schema#",
-    "title": "json-schema-validator-url-mapping",
+    "title": "json-schema-validator-uri-mapping",
     "type": "array",
     "description": "Data portion of message from JMRI to client for type \"node\"",
     "items": {