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.json; 014 015import java.util.*; 016import java.util.concurrent.*; 017 018import org.apache.juneau.*; 019import org.apache.juneau.annotation.*; 020import org.apache.juneau.jsonschema.*; 021import org.apache.juneau.serializer.*; 022 023/** 024 * Serializes POJO metadata to HTTP responses as JSON-Schema. 025 * 026 * <h5 class='topic'>Media types</h5> 027 * 028 * Handles <c>Accept</c> types: <bc>application/json+schema, text/json+schema</bc> 029 * <p> 030 * Produces <c>Content-Type</c> types: <bc>application/json</bc> 031 * 032 * <h5 class='topic'>Description</h5> 033 * 034 * Produces the JSON-schema for the JSON produced by the {@link JsonSerializer} class with the same properties. 035 */ 036@ConfigurableContext 037public class JsonSchemaSerializer extends JsonSerializer implements JsonSchemaMetaProvider { 038 039 //------------------------------------------------------------------------------------------------------------------- 040 // Configurable properties 041 //------------------------------------------------------------------------------------------------------------------- 042 043 static final String PREFIX = "JsonSchemaSerializer"; 044 045 //------------------------------------------------------------------------------------------------------------------- 046 // Predefined instances 047 //------------------------------------------------------------------------------------------------------------------- 048 049 /** Default serializer, all default settings.*/ 050 public static final JsonSchemaSerializer DEFAULT = new JsonSchemaSerializer(PropertyStore.DEFAULT); 051 052 /** Default serializer, all default settings.*/ 053 public static final JsonSchemaSerializer DEFAULT_READABLE = new Readable(PropertyStore.DEFAULT); 054 055 /** Default serializer, single quotes, simple mode. */ 056 public static final JsonSchemaSerializer DEFAULT_SIMPLE = new Simple(PropertyStore.DEFAULT); 057 058 /** Default serializer, single quotes, simple mode, with whitespace. */ 059 public static final JsonSchemaSerializer DEFAULT_SIMPLE_READABLE = new SimpleReadable(PropertyStore.DEFAULT); 060 061 062 //------------------------------------------------------------------------------------------------------------------- 063 // Predefined subclasses 064 //------------------------------------------------------------------------------------------------------------------- 065 066 /** Default serializer, with whitespace. */ 067 public static class Readable extends JsonSchemaSerializer { 068 069 /** 070 * Constructor. 071 * 072 * @param ps The property store containing all the settings for this object. 073 */ 074 public Readable(PropertyStore ps) { 075 super( 076 ps.builder().set(WSERIALIZER_useWhitespace, true).build() 077 ); 078 } 079 } 080 081 /** Default serializer, single quotes, simple mode. */ 082 public static class Simple extends JsonSchemaSerializer { 083 084 /** 085 * Constructor. 086 * 087 * @param ps The property store containing all the settings for this object. 088 */ 089 public Simple(PropertyStore ps) { 090 super( 091 ps.builder() 092 .set(JSON_simpleMode, true) 093 .set(WSERIALIZER_quoteChar, '\'') 094 .build() 095 ); 096 } 097 } 098 099 /** Default serializer, single quotes, simple mode, with whitespace. */ 100 public static class SimpleReadable extends JsonSchemaSerializer { 101 102 /** 103 * Constructor. 104 * 105 * @param ps The property store containing all the settings for this object. 106 */ 107 public SimpleReadable(PropertyStore ps) { 108 super( 109 ps.builder() 110 .set(JSON_simpleMode, true) 111 .set(WSERIALIZER_quoteChar, '\'') 112 .set(WSERIALIZER_useWhitespace, true) 113 .build() 114 ); 115 } 116 } 117 118 119 //------------------------------------------------------------------------------------------------------------------- 120 // Instance 121 //------------------------------------------------------------------------------------------------------------------- 122 123 private final JsonSchemaGenerator generator; 124 private final Map<ClassMeta<?>,JsonSchemaClassMeta> jsonSchemaClassMetas = new ConcurrentHashMap<>(); 125 private final Map<BeanPropertyMeta,JsonSchemaBeanPropertyMeta> jsonSchemaBeanPropertyMetas = new ConcurrentHashMap<>(); 126 127 /** 128 * Constructor. 129 * 130 * @param ps Initialize with the specified config property store. 131 */ 132 public JsonSchemaSerializer(PropertyStore ps) { 133 super( 134 ps.builder() 135 .set(BEANTRAVERSE_detectRecursions, true) 136 .set(BEANTRAVERSE_ignoreRecursions, true) 137 .build(), 138 "application/json", "application/json+schema,text/json+schema" 139 ); 140 141 generator = JsonSchemaGenerator.create().apply(getPropertyStore()).build(); 142 } 143 144 @Override /* Context */ 145 public JsonSchemaSerializerBuilder builder() { 146 return new JsonSchemaSerializerBuilder(getPropertyStore()); 147 } 148 149 /** 150 * Instantiates a new clean-slate {@link JsonSerializerBuilder} object. 151 * 152 * <p> 153 * This is equivalent to simply calling <code><jk>new</jk> JsonSerializerBuilder()</code>. 154 * 155 * @return A new {@link JsonSerializerBuilder} object. 156 */ 157 public static JsonSchemaSerializerBuilder create() { 158 return new JsonSchemaSerializerBuilder(); 159 } 160 161 @Override /* Context */ 162 public JsonSchemaSerializerSession createSession() { 163 return createSession(createDefaultSessionArgs()); 164 } 165 166 @Override /* Serializer */ 167 public JsonSchemaSerializerSession createSession(SerializerSessionArgs args) { 168 return new JsonSchemaSerializerSession(this, args); 169 } 170 171 JsonSchemaGenerator getGenerator() { 172 return generator; 173 } 174 175 //----------------------------------------------------------------------------------------------------------------- 176 // Extended metadata 177 //----------------------------------------------------------------------------------------------------------------- 178 179 @Override /* JsonSchemaMetaProvider */ 180 public JsonSchemaClassMeta getJsonSchemaClassMeta(ClassMeta<?> cm) { 181 JsonSchemaClassMeta m = jsonSchemaClassMetas.get(cm); 182 if (m == null) { 183 m = new JsonSchemaClassMeta(cm, this); 184 jsonSchemaClassMetas.put(cm, m); 185 } 186 return m; 187 } 188 189 @Override /* JsonSchemaMetaProvider */ 190 public JsonSchemaBeanPropertyMeta getJsonSchemaBeanPropertyMeta(BeanPropertyMeta bpm) { 191 JsonSchemaBeanPropertyMeta m = jsonSchemaBeanPropertyMetas.get(bpm); 192 if (m == null) { 193 m = new JsonSchemaBeanPropertyMeta(bpm.getDelegateFor(), this); 194 jsonSchemaBeanPropertyMetas.put(bpm, m); 195 } 196 return m; 197 } 198 199 //----------------------------------------------------------------------------------------------------------------- 200 // Other methods 201 //----------------------------------------------------------------------------------------------------------------- 202 203 @Override /* Context */ 204 public ObjectMap toMap() { 205 return super.toMap() 206 .append("JsonSchemaSerializer", new DefaultFilteringObjectMap() 207 .append("generator", generator) 208 ); 209 } 210}