001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.juneau.bean.jsonschema; 018 019import static org.apache.juneau.common.utils.StringUtils.*; 020import static org.apache.juneau.common.utils.ThrowableUtils.*; 021 022import java.io.*; 023import java.net.*; 024import java.util.concurrent.*; 025 026import org.apache.juneau.*; 027import org.apache.juneau.json.*; 028 029/** 030 * A container for retrieving JSON {@link JsonSchema} objects by URI. 031 * 032 * <p> 033 * Subclasses must implement one of the following methods to load schemas from external sources: 034 * <ul class='spaced-list'> 035 * <li> 036 * {@link #getReader(URI)} - If schemas should be loaded from readers and automatically parsed. 037 * <li> 038 * {@link #load(URI)} - If you want control over construction of {@link JsonSchema} objects. 039 * </ul> 040 * 041 * @serial exclude 042 */ 043public abstract class JsonSchemaMap extends ConcurrentHashMap<URI,JsonSchema> { 044 045 private static final long serialVersionUID = 1L; 046 047 /** 048 * Return the {@link JsonSchema} object at the specified URI. 049 * 050 * <p> 051 * If this schema object has not been loaded yet, calls {@link #load(URI)}. 052 * 053 * <p> 054 * The value can be of any of the following types: {@link URI}, {@link URL}, {@link String}. 055 * Strings must be valid URIs. 056 * 057 * <p> 058 * URIs defined by {@link UriResolver} can be used for values. 059 * 060 * @param uri The URI of the schema to retrieve. 061 * @return The JsonSchema, or <jk>null</jk> if schema was not located and could not be loaded. 062 */ 063 @Override /* Map */ 064 public JsonSchema get(Object uri) { 065 var u = toURI(uri); 066 var s = super.get(u); 067 if (s != null) 068 return s; 069 synchronized(this) { 070 s = load(u); 071 if (s != null) { 072 // Note: Can't use add(Schema...) since the ID property may not be set. 073 s.setSchemaMap(this); 074 put(u, s); 075 } 076 return s; 077 } 078 } 079 080 /** 081 * Convenience method for pre-populating this map with the specified schemas. 082 * 083 * <p> 084 * The schemas passed in through this method MUST have their ID properties set. 085 * 086 * @param schemas The set of schemas to add to this map. 087 * @return This object. 088 * @throws RuntimeException If one or more schema objects did not have their ID property set. 089 */ 090 public JsonSchemaMap add(JsonSchema...schemas) { 091 for (var schema : schemas) { 092 if (schema.getId() == null) 093 throw new IllegalArgumentException("Schema with no ID passed to JsonSchemaMap.add(Schema...)"); 094 put(schema.getId(), schema); 095 schema.setSchemaMap(this); 096 } 097 return this; 098 } 099 100 /** 101 * Subclasses must implement either this method or {@link #getReader(URI)} to load the schema with the specified URI. 102 * 103 * <p> 104 * It's up to the implementer to decide where these come from. 105 * 106 * <p> 107 * The default implementation calls {@link #getReader(URI)} and parses the schema document. 108 * If {@link #getReader(URI)} returns <jk>null</jk>, this method returns <jk>null</jk> indicating this is an 109 * unreachable document. 110 * 111 * @param uri The URI to load the schema from. 112 * @return The parsed schema. 113 */ 114 public JsonSchema load(URI uri) { 115 try (var r = getReader(uri)) { 116 if (r == null) 117 return null; 118 return JsonParser.DEFAULT.parse(r, JsonSchema.class); 119 } catch (Exception e) { 120 throw asRuntimeException(e); 121 } 122 } 123 124 /** 125 * Subclasses must implement either this method or {@link #load(URI)} to load the schema with the specified URI. 126 * 127 * <p> 128 * It's up to the implementer to decide where these come from. 129 * 130 * <p> 131 * The default implementation returns <jk>null</jk>. 132 * 133 * @param uri The URI to connect to and retrieve the contents. 134 * @return The reader from reading the specified URI. 135 */ 136 public Reader getReader(URI uri) { 137 return null; 138 } 139}