001/* 002 * ModeShape (http://www.modeshape.org) 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.modeshape.schematic.internal.schema; 017 018import java.io.Serializable; 019import java.net.URI; 020import java.net.URISyntaxException; 021import java.util.Map; 022import java.util.concurrent.ConcurrentHashMap; 023import org.modeshape.schematic.DocumentLibrary; 024import org.modeshape.schematic.annotation.ThreadSafe; 025import org.modeshape.schematic.document.Document; 026import org.modeshape.schematic.document.JsonSchema; 027import org.modeshape.schematic.document.Path; 028import org.modeshape.schematic.internal.document.Paths; 029 030/** 031 * A cache of {@link SchemaDocument} instances that each have a {@link SchemaDocument#getValidator() validator} that can 032 * {@link Validator#validate(Object, String, Document, Path, Problems, Validator.SchemaDocumentResolver) 033 * validate} JSON Documents. When a SchemaDocument is needed and has not yet been loaded, this cache finds the corresponding JSON 034 * Schema {@link Document documents} from the {@link DocumentLibrary}, and uses a {@link Validator.Factory factory} to create the 035 * {@link Validator}. 036 * 037 * @author Randall Hauch <rhauch@redhat.com> (C) 2011 Red Hat Inc. 038 */ 039@ThreadSafe 040public class SchemaDocumentCache implements Validator.SchemaDocumentResolver, Serializable { 041 042 private static final long serialVersionUID = 1L; 043 044 private final String defaultMetaSchemaUri; 045 private final DocumentLibrary jsonSchemaDocuments; 046 private transient Map<String, SchemaDocument> schemaDocumentsByUri = new ConcurrentHashMap<>(); 047 048 public SchemaDocumentCache( DocumentLibrary jsonSchemaDocuments, 049 String defaultMetaSchemaUri ) { 050 this.jsonSchemaDocuments = jsonSchemaDocuments; 051 this.defaultMetaSchemaUri = defaultMetaSchemaUri != null ? defaultMetaSchemaUri : JsonSchema.Version.Latest.CORE_METASCHEMA_URL; 052 } 053 054 /** 055 * Find the JSON Schema document with the supplied URI, and build the {@link SchemaDocument in-memory representation} 056 * (including the {@link Validator validator}. 057 * 058 * @param uri the URI of the schema being read 059 * @param problems the object for recording any problems encountered while loading the schema 060 * @return the in-memory {@link Document} representation, or null if there were fatal errors 061 */ 062 @Override 063 public SchemaDocument get( String uri, 064 Problems problems ) { 065 if (uri == null) { 066 throw new IllegalArgumentException("The 'uri' parameter may not be null"); 067 } 068 069 // Check the cache first ... 070 SchemaDocument result = schemaDocumentsByUri.get(uri); 071 if (result != null) { 072 return result; 073 } 074 075 // Find the JSON Schema document ... 076 Document doc = jsonSchemaDocuments.get(uri); 077 if (doc == null) { 078 problems.recordError(Paths.rootPath(), "Unable to find the JSON Schema document for '" + uri + "'"); 079 return null; 080 } 081 082 // Validate the JSON document, if required ... 083 String id = doc.getString("id"); 084 String schemaRef = doc.getString("$schema"); 085 if (schemaRef == null) { 086 schemaRef = defaultMetaSchemaUri; 087 } 088 if (!schemaRef.equals(id)) { 089 // This is not the meta-schema, so we need to validate it ... 090 SchemaDocument schemaOfSchema = get(schemaRef, problems); 091 Document schemaOfSchemaDoc = schemaOfSchema.getDocument(); 092 schemaOfSchema.getValidator().validate(null, null, schemaOfSchemaDoc, Paths.rootPath(), problems, this); 093 } 094 095 // The schema was valid ... 096 URI schemaRefUri = null; 097 try { 098 schemaRefUri = new URI(uri); 099 } catch (URISyntaxException e) { 100 problems.recordWarning(Paths.path("$schema"), "The URI of the referenced schema '" + uri + "' is not a valid URI"); 101 } 102 103 // Now build a validator for this JSON Schema (if we can) ... 104 Validator validator = createFactory(schemaRefUri, problems).create(doc, Paths.rootPath()); 105 if (validator != null) { 106 // Create the schema representation and cache it ... 107 result = new SchemaDocument(schemaRef, doc, validator); 108 schemaDocumentsByUri.put(uri, result); 109 } 110 111 return result; 112 } 113 114 /** 115 * Remove any {@link SchemaDocument} that was loaded into this cache. 116 * 117 * @param uri the URI of the schema being read 118 * @return true if a SchemaDocument was removed from the cache, or false if this cache did not contain a SchemaDocument for 119 * the supplied URI 120 */ 121 public boolean remove( String uri ) { 122 return schemaDocumentsByUri.remove(uri) != null; 123 } 124 125 /** 126 * Remove all {@link SchemaDocument} loaded into this cache. 127 */ 128 public void removeAll() { 129 schemaDocumentsByUri.clear(); 130 } 131 132 protected Validator.Factory createFactory( URI schemaRefUri, 133 Problems problems ) { 134 return new JsonSchemaValidatorFactory(schemaRefUri, problems); 135 } 136}