1*78c4dd6aSAndroid Build Coastguard Worker# Customizing Schema Retrieval 2*78c4dd6aSAndroid Build Coastguard Worker 3*78c4dd6aSAndroid Build Coastguard WorkerA schema can be identified by its schema identifier which is indicated using the `$id` keyword or `id` keyword in earlier drafts. This is an absolute IRI that uniquely identifies the schema and is not necessarily a network locator. A schema need not be downloadable from it's absolute IRI. 4*78c4dd6aSAndroid Build Coastguard Worker 5*78c4dd6aSAndroid Build Coastguard WorkerIn the event a schema references a schema identifier that is not a subschema resource, for instance defined in the `$defs` keyword or `definitions` keyword. The library will need to be able to retrieve the schema given its schema identifier. 6*78c4dd6aSAndroid Build Coastguard Worker 7*78c4dd6aSAndroid Build Coastguard WorkerIn the event that the schema does not define a schema identifier using the `$id` keyword, the retrieval IRI will be used as it's schema identifier. 8*78c4dd6aSAndroid Build Coastguard Worker 9*78c4dd6aSAndroid Build Coastguard Worker## Loading Schemas from memory 10*78c4dd6aSAndroid Build Coastguard Worker 11*78c4dd6aSAndroid Build Coastguard WorkerSchemas can be loaded through a map. 12*78c4dd6aSAndroid Build Coastguard Worker 13*78c4dd6aSAndroid Build Coastguard Worker```java 14*78c4dd6aSAndroid Build Coastguard WorkerString schemaData = "{\r\n" 15*78c4dd6aSAndroid Build Coastguard Worker + " \"type\": \"integer\"\r\n" 16*78c4dd6aSAndroid Build Coastguard Worker + "}"; 17*78c4dd6aSAndroid Build Coastguard WorkerMap<String, String> schemas = Collections.singletonMap("https://www.example.com/integer.json", schemaData); 18*78c4dd6aSAndroid Build Coastguard WorkerJsonSchemaFactory schemaFactory = JsonSchemaFactory 19*78c4dd6aSAndroid Build Coastguard Worker .getInstance(VersionFlag.V7, 20*78c4dd6aSAndroid Build Coastguard Worker builder -> builder.schemaLoaders(schemaLoaders -> schemaLoaders.schemas(schemas))); 21*78c4dd6aSAndroid Build Coastguard Worker``` 22*78c4dd6aSAndroid Build Coastguard Worker 23*78c4dd6aSAndroid Build Coastguard WorkerSchemas can be loaded through a function. 24*78c4dd6aSAndroid Build Coastguard Worker 25*78c4dd6aSAndroid Build Coastguard Worker```java 26*78c4dd6aSAndroid Build Coastguard WorkerString schemaData = "{\r\n" 27*78c4dd6aSAndroid Build Coastguard Worker + " \"type\": \"integer\"\r\n" 28*78c4dd6aSAndroid Build Coastguard Worker + "}"; 29*78c4dd6aSAndroid Build Coastguard WorkerMap<String, String> schemas = Collections.singletonMap("https://www.example.com/integer.json", schemaData); 30*78c4dd6aSAndroid Build Coastguard Worker JsonSchemaFactory schemaFactory = JsonSchemaFactory 31*78c4dd6aSAndroid Build Coastguard Worker .getInstance(VersionFlag.V7, 32*78c4dd6aSAndroid Build Coastguard Worker builder -> builder.schemaLoaders(schemaLoaders -> schemaLoaders.schemas(schemas::get))); 33*78c4dd6aSAndroid Build Coastguard Worker``` 34*78c4dd6aSAndroid Build Coastguard Worker 35*78c4dd6aSAndroid Build Coastguard WorkerSchemas can also be loaded in the following manner. 36*78c4dd6aSAndroid Build Coastguard Worker 37*78c4dd6aSAndroid Build Coastguard Worker```java 38*78c4dd6aSAndroid Build Coastguard Workerclass RegistryEntry { 39*78c4dd6aSAndroid Build Coastguard Worker private final String schemaData; 40*78c4dd6aSAndroid Build Coastguard Worker 41*78c4dd6aSAndroid Build Coastguard Worker public RegistryEntry(String schemaData) { 42*78c4dd6aSAndroid Build Coastguard Worker this.schemaData = schemaData; 43*78c4dd6aSAndroid Build Coastguard Worker } 44*78c4dd6aSAndroid Build Coastguard Worker 45*78c4dd6aSAndroid Build Coastguard Worker public String getSchemaData() { 46*78c4dd6aSAndroid Build Coastguard Worker return this.schemaData; 47*78c4dd6aSAndroid Build Coastguard Worker } 48*78c4dd6aSAndroid Build Coastguard Worker} 49*78c4dd6aSAndroid Build Coastguard Worker 50*78c4dd6aSAndroid Build Coastguard WorkerString schemaData = "{\r\n" 51*78c4dd6aSAndroid Build Coastguard Worker + " \"type\": \"integer\"\r\n" 52*78c4dd6aSAndroid Build Coastguard Worker + "}"; 53*78c4dd6aSAndroid Build Coastguard WorkerMap<String, RegistryEntry> registry = Collections 54*78c4dd6aSAndroid Build Coastguard Worker .singletonMap("https://www.example.com/integer.json", new RegistryEntry(schemaData)); 55*78c4dd6aSAndroid Build Coastguard WorkerJsonSchemaFactory schemaFactory = JsonSchemaFactory 56*78c4dd6aSAndroid Build Coastguard Worker .getInstance(VersionFlag.V7, builder -> builder 57*78c4dd6aSAndroid Build Coastguard Worker .schemaLoaders(schemaLoaders -> schemaLoaders.schemas(registry::get, RegistryEntry::getSchemaData))); 58*78c4dd6aSAndroid Build Coastguard Worker``` 59*78c4dd6aSAndroid Build Coastguard Worker 60*78c4dd6aSAndroid Build Coastguard Worker## Mapping Schema Identifier to Retrieval IRI 61*78c4dd6aSAndroid Build Coastguard Worker 62*78c4dd6aSAndroid Build Coastguard WorkerThe schema identifier can be mapped to the retrieval IRI by implementing the `SchemaMapper` interface. 63*78c4dd6aSAndroid Build Coastguard Worker 64*78c4dd6aSAndroid Build Coastguard Worker### Configuring Schema Mapper 65*78c4dd6aSAndroid Build Coastguard Worker 66*78c4dd6aSAndroid Build Coastguard Worker```java 67*78c4dd6aSAndroid Build Coastguard Workerclass CustomSchemaMapper implements SchemaMapper { 68*78c4dd6aSAndroid Build Coastguard Worker @Override 69*78c4dd6aSAndroid Build Coastguard Worker public AbsoluteIri map(AbsoluteIri absoluteIRI) { 70*78c4dd6aSAndroid Build Coastguard Worker String iri = absoluteIRI.toString(); 71*78c4dd6aSAndroid Build Coastguard Worker if ("https://www.example.com/integer.json".equals(iri)) { 72*78c4dd6aSAndroid Build Coastguard Worker return AbsoluteIri.of("classpath:schemas/integer.json"); 73*78c4dd6aSAndroid Build Coastguard Worker } 74*78c4dd6aSAndroid Build Coastguard Worker return null; 75*78c4dd6aSAndroid Build Coastguard Worker } 76*78c4dd6aSAndroid Build Coastguard Worker} 77*78c4dd6aSAndroid Build Coastguard Worker 78*78c4dd6aSAndroid Build Coastguard WorkerJsonSchemaFactory schemaFactory = JsonSchemaFactory 79*78c4dd6aSAndroid Build Coastguard Worker .getInstance(VersionFlag.V7, 80*78c4dd6aSAndroid Build Coastguard Worker builder -> builder.schemaMappers(schemaMappers -> schemaMappers.add(new CustomSchemaMapper()))); 81*78c4dd6aSAndroid Build Coastguard Worker``` 82*78c4dd6aSAndroid Build Coastguard Worker 83*78c4dd6aSAndroid Build Coastguard Worker### Configuring Prefix Mappings 84*78c4dd6aSAndroid Build Coastguard Worker 85*78c4dd6aSAndroid Build Coastguard Worker```java 86*78c4dd6aSAndroid Build Coastguard WorkerJsonSchemaFactory schemaFactory = JsonSchemaFactory 87*78c4dd6aSAndroid Build Coastguard Worker .getInstance(VersionFlag.V7, 88*78c4dd6aSAndroid Build Coastguard Worker builder -> builder 89*78c4dd6aSAndroid Build Coastguard Worker .schemaMappers(schemaMappers -> schemaMappers 90*78c4dd6aSAndroid Build Coastguard Worker .mapPrefix("https://json-schema.org", "classpath:") 91*78c4dd6aSAndroid Build Coastguard Worker .mapPrefix("http://json-schema.org", "classpath:"))); 92*78c4dd6aSAndroid Build Coastguard Worker``` 93*78c4dd6aSAndroid Build Coastguard Worker 94*78c4dd6aSAndroid Build Coastguard Worker### Configuring Mappings 95*78c4dd6aSAndroid Build Coastguard Worker 96*78c4dd6aSAndroid Build Coastguard Worker```java 97*78c4dd6aSAndroid Build Coastguard WorkerMap<String, String> mappings = Collections 98*78c4dd6aSAndroid Build Coastguard Worker .singletonMap("https://www.example.com/integer.json", "classpath:schemas/integer.json"); 99*78c4dd6aSAndroid Build Coastguard Worker 100*78c4dd6aSAndroid Build Coastguard WorkerJsonSchemaFactory schemaFactory = JsonSchemaFactory 101*78c4dd6aSAndroid Build Coastguard Worker .getInstance(VersionFlag.V7, 102*78c4dd6aSAndroid Build Coastguard Worker builder -> builder.schemaMappers(schemaMappers -> schemaMappers.mappings(mappings))); 103*78c4dd6aSAndroid Build Coastguard Worker``` 104*78c4dd6aSAndroid Build Coastguard Worker 105*78c4dd6aSAndroid Build Coastguard Worker## Customizing Network Schema Retrieval 106*78c4dd6aSAndroid Build Coastguard Worker 107*78c4dd6aSAndroid Build Coastguard WorkerThe default `UriSchemaLoader` implementation uses JDK connection/socket without handling network exceptions. It works in most of the cases; however, if you want to have a customized implementation, you can do so. One user has his implementation with urirest to handle the timeout. A detailed discussion can be found in this [issue](https://github.com/networknt/json-schema-validator/issues/240) 108*78c4dd6aSAndroid Build Coastguard Worker 109*78c4dd6aSAndroid Build Coastguard Worker### Configuring Custom URI Schema Loader 110*78c4dd6aSAndroid Build Coastguard Worker 111*78c4dd6aSAndroid Build Coastguard WorkerThe default `UriSchemaLoader` can be overwritten in order to customize its behaviour in regards of authorization or error handling. 112*78c4dd6aSAndroid Build Coastguard Worker 113*78c4dd6aSAndroid Build Coastguard WorkerThe `SchemaLoader` interface must implemented and the implementation configured on the `JsonSchemaFactory`. 114*78c4dd6aSAndroid Build Coastguard Worker 115*78c4dd6aSAndroid Build Coastguard Worker```java 116*78c4dd6aSAndroid Build Coastguard Workerpublic class CustomUriSchemaLoader implements SchemaLoader { 117*78c4dd6aSAndroid Build Coastguard Worker private static final Logger LOGGER = LoggerFactory.getLogger(CustomUriSchemaLoader.class); 118*78c4dd6aSAndroid Build Coastguard Worker private final String authorizationToken; 119*78c4dd6aSAndroid Build Coastguard Worker private final HttpClient client; 120*78c4dd6aSAndroid Build Coastguard Worker 121*78c4dd6aSAndroid Build Coastguard Worker public CustomUriSchemaLoader(String authorizationToken) { 122*78c4dd6aSAndroid Build Coastguard Worker this.authorizationToken = authorizationToken; 123*78c4dd6aSAndroid Build Coastguard Worker this.client = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(10)).build(); 124*78c4dd6aSAndroid Build Coastguard Worker } 125*78c4dd6aSAndroid Build Coastguard Worker 126*78c4dd6aSAndroid Build Coastguard Worker @Override 127*78c4dd6aSAndroid Build Coastguard Worker public InputStreamSource getSchema(AbsoluteIri absoluteIri) { 128*78c4dd6aSAndroid Build Coastguard Worker String scheme = absoluteIri.getScheme(); 129*78c4dd6aSAndroid Build Coastguard Worker if ("https".equals(scheme) || "http".equals(scheme)) { 130*78c4dd6aSAndroid Build Coastguard Worker URI uri = URI.create(absoluteIri.toString()); 131*78c4dd6aSAndroid Build Coastguard Worker return () -> { 132*78c4dd6aSAndroid Build Coastguard Worker HttpRequest request = HttpRequest.newBuilder().uri(uri).header("Authorization", authorizationToken).build(); 133*78c4dd6aSAndroid Build Coastguard Worker try { 134*78c4dd6aSAndroid Build Coastguard Worker HttpResponse<String> response = this.client.send(request, HttpResponse.BodyHandlers.ofString()); 135*78c4dd6aSAndroid Build Coastguard Worker if ((200 > response.statusCode()) || (response.statusCode() > 299)) { 136*78c4dd6aSAndroid Build Coastguard Worker String errorMessage = String.format("Could not get data from schema endpoint. The following status %d was returned.", response.statusCode()); 137*78c4dd6aSAndroid Build Coastguard Worker LOGGER.error(errorMessage); 138*78c4dd6aSAndroid Build Coastguard Worker } 139*78c4dd6aSAndroid Build Coastguard Worker return new ByteArrayInputStream(response.body().getBytes(StandardCharsets.UTF_8)); 140*78c4dd6aSAndroid Build Coastguard Worker } catch (InterruptedException e) { 141*78c4dd6aSAndroid Build Coastguard Worker throw new RuntimeException(e); 142*78c4dd6aSAndroid Build Coastguard Worker } 143*78c4dd6aSAndroid Build Coastguard Worker } 144*78c4dd6aSAndroid Build Coastguard Worker } 145*78c4dd6aSAndroid Build Coastguard Worker return null; 146*78c4dd6aSAndroid Build Coastguard Worker } 147*78c4dd6aSAndroid Build Coastguard Worker} 148*78c4dd6aSAndroid Build Coastguard Worker``` 149*78c4dd6aSAndroid Build Coastguard Worker 150*78c4dd6aSAndroid Build Coastguard WorkerWithin the `JsonSchemaFactory` the custom `SchemaLoader` must be configured. 151*78c4dd6aSAndroid Build Coastguard Worker 152*78c4dd6aSAndroid Build Coastguard Worker```java 153*78c4dd6aSAndroid Build Coastguard WorkerCustomUriSchemaLoader uriSchemaLoader = new CustomUriSchemaLoader(authorizationToken); 154*78c4dd6aSAndroid Build Coastguard Worker 155*78c4dd6aSAndroid Build Coastguard WorkerJsonSchemaFactory schemaFactory = JsonSchemaFactory 156*78c4dd6aSAndroid Build Coastguard Worker .getInstance(VersionFlag.V7, 157*78c4dd6aSAndroid Build Coastguard Worker builder -> builder.schemaLoaders(schemaLoaders -> schemaLoaders.add(uriSchemaLoader))); 158*78c4dd6aSAndroid Build Coastguard Worker``` 159