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.commons.utils.StringUtils.*; 020import static org.apache.juneau.commons.utils.ThrowableUtils.*; 021import static org.apache.juneau.commons.utils.Utils.*; 022 023import java.io.*; 024import java.net.*; 025import java.util.concurrent.*; 026 027import org.apache.juneau.*; 028import org.apache.juneau.json.*; 029 030/** 031 * A container for retrieving JSON {@link JsonSchema} objects by URI. 032 * 033 * <p> 034 * Subclasses must implement one of the following methods to load schemas from external sources: 035 * <ul class='spaced-list'> 036 * <li> 037 * {@link #getReader(URI)} - If schemas should be loaded from readers and automatically parsed. 038 * <li> 039 * {@link #load(URI)} - If you want control over construction of {@link JsonSchema} objects. 040 * </ul> 041 * 042 * @serial exclude 043 */ 044public abstract class JsonSchemaMap extends ConcurrentHashMap<URI,JsonSchema> { 045 046 private static final long serialVersionUID = 1L; 047 048 /** 049 * Convenience method for pre-populating this map with the specified schemas. 050 * 051 * <p> 052 * The schemas passed in through this method MUST have their ID properties set. 053 * 054 * @param schemas The set of schemas to add to this map. 055 * @return This object. 056 * @throws RuntimeException If one or more schema objects did not have their ID property set. 057 */ 058 @SuppressWarnings("deprecation") 059 public JsonSchemaMap add(JsonSchema...schemas) { 060 for (var schema : schemas) { 061 if (schema.getId() == null) 062 throw illegalArg("Schema with no ID passed to JsonSchemaMap.add(Schema...)"); 063 put(schema.getId(), schema); 064 schema.setSchemaMap(this); 065 } 066 return this; 067 } 068 069 /** 070 * Return the {@link JsonSchema} object at the specified URI. 071 * 072 * <p> 073 * If this schema object has not been loaded yet, calls {@link #load(URI)}. 074 * 075 * <p> 076 * The value can be of any of the following types: {@link URI}, {@link URL}, {@link String}. 077 * Strings must be valid URIs. 078 * 079 * <p> 080 * URIs defined by {@link UriResolver} can be used for values. 081 * 082 * @param uri The URI of the schema to retrieve. 083 * @return The JsonSchema, or <jk>null</jk> if schema was not located and could not be loaded. 084 */ 085 @Override /* Overridden from Map */ 086 public JsonSchema get(Object uri) { 087 var u = toUri(uri); 088 var s = super.get(u); 089 if (nn(s)) 090 return s; 091 synchronized (this) { 092 s = load(u); 093 if (nn(s)) { 094 // Note: Can't use add(Schema...) since the ID property may not be set. 095 s.setSchemaMap(this); 096 put(u, s); 097 } 098 return s; 099 } 100 } 101 102 /** 103 * Subclasses must implement either this method or {@link #load(URI)} to load the schema with the specified URI. 104 * 105 * <p> 106 * It's up to the implementer to decide where these come from. 107 * 108 * <p> 109 * The default implementation returns <jk>null</jk>. 110 * 111 * @param uri The URI to connect to and retrieve the contents. 112 * @return The reader from reading the specified URI. 113 */ 114 public Reader getReader(URI uri) { 115 return null; 116 } 117 118 /** 119 * Subclasses must implement either this method or {@link #getReader(URI)} to load the schema with the specified URI. 120 * 121 * <p> 122 * It's up to the implementer to decide where these come from. 123 * 124 * <p> 125 * The default implementation calls {@link #getReader(URI)} and parses the schema document. 126 * If {@link #getReader(URI)} returns <jk>null</jk>, this method returns <jk>null</jk> indicating this is an 127 * unreachable document. 128 * 129 * @param uri The URI to load the schema from. 130 * @return The parsed schema. 131 */ 132 public JsonSchema load(URI uri) { 133 try (var r = getReader(uri)) { 134 if (r == null) 135 return null; 136 return JsonParser.DEFAULT.parse(r, JsonSchema.class); 137 } catch (Exception e) { 138 throw toRex(e); 139 } 140 } 141}