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