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.camel.processor.validation; 018 019import java.io.ByteArrayInputStream; 020import java.io.File; 021import java.io.IOException; 022import java.io.InputStream; 023import java.net.URL; 024 025import javax.xml.XMLConstants; 026import javax.xml.transform.Source; 027import javax.xml.transform.stream.StreamSource; 028import javax.xml.validation.Schema; 029import javax.xml.validation.SchemaFactory; 030 031import org.w3c.dom.ls.LSResourceResolver; 032 033import org.xml.sax.SAXException; 034 035import org.apache.camel.CamelContext; 036import org.apache.camel.converter.IOConverter; 037import org.apache.camel.util.IOHelper; 038import org.apache.camel.util.ObjectHelper; 039import org.apache.camel.util.ResourceHelper; 040import org.slf4j.Logger; 041import org.slf4j.LoggerFactory; 042 043/** 044 * Reads the schema used in the processor {@link ValidatingProcessor}. 045 * A schema re-reading could be forced using {@link org.apache.camel.component.validator.ValidatorEndpoint#clearCachedSchema()}. 046 */ 047public class SchemaReader { 048 049 /** Key of the global option to switch either off or on the access to external DTDs in the XML Validator for StreamSources. 050 * Only effective, if not a custom schema factory is used.*/ 051 public static final String ACCESS_EXTERNAL_DTD = "CamelXmlValidatorAccessExternalDTD"; 052 053 private static final Logger LOG = LoggerFactory.getLogger(SchemaReader.class); 054 055 private String schemaLanguage = XMLConstants.W3C_XML_SCHEMA_NS_URI; 056 // must be volatile because is accessed from different threads see ValidatorEndpoint.clearCachedSchema 057 private volatile Schema schema; 058 private Source schemaSource; 059 // must be volatile because is accessed from different threads see ValidatorEndpoint.clearCachedSchema 060 private volatile SchemaFactory schemaFactory; 061 private URL schemaUrl; 062 private File schemaFile; 063 private byte[] schemaAsByteArray; 064 private final String schemaResourceUri; 065 private LSResourceResolver resourceResolver; 066 067 private final CamelContext camelContext; 068 069 070 public SchemaReader() { 071 this.camelContext = null; 072 this.schemaResourceUri = null; 073 } 074 075 /** Specify a camel context and a schema resource URI in order to read the schema via the class resolver specified in the Camel context. */ 076 public SchemaReader(CamelContext camelContext, String schemaResourceUri) { 077 ObjectHelper.notNull(camelContext, "camelContext"); 078 ObjectHelper.notNull(schemaResourceUri, "schemaResourceUri"); 079 this.camelContext = camelContext; 080 this.schemaResourceUri = schemaResourceUri; 081 } 082 083 public void loadSchema() throws Exception { 084 // force loading of schema 085 schema = createSchema(); 086 } 087 088 // Properties 089 // ----------------------------------------------------------------------- 090 091 public Schema getSchema() throws IOException, SAXException { 092 if (schema == null) { 093 synchronized (this) { 094 if (schema == null) { 095 schema = createSchema(); 096 } 097 } 098 } 099 return schema; 100 } 101 102 public void setSchema(Schema schema) { 103 this.schema = schema; 104 } 105 106 public String getSchemaLanguage() { 107 return schemaLanguage; 108 } 109 110 public void setSchemaLanguage(String schemaLanguage) { 111 this.schemaLanguage = schemaLanguage; 112 } 113 114 public Source getSchemaSource() throws IOException { 115 if (schemaSource == null) { 116 schemaSource = createSchemaSource(); 117 } 118 return schemaSource; 119 } 120 121 public void setSchemaSource(Source schemaSource) { 122 this.schemaSource = schemaSource; 123 } 124 125 public URL getSchemaUrl() { 126 return schemaUrl; 127 } 128 129 public void setSchemaUrl(URL schemaUrl) { 130 this.schemaUrl = schemaUrl; 131 } 132 133 public File getSchemaFile() { 134 return schemaFile; 135 } 136 137 public void setSchemaFile(File schemaFile) { 138 this.schemaFile = schemaFile; 139 } 140 141 public byte[] getSchemaAsByteArray() { 142 return schemaAsByteArray; 143 } 144 145 public void setSchemaAsByteArray(byte[] schemaAsByteArray) { 146 this.schemaAsByteArray = schemaAsByteArray; 147 } 148 149 public SchemaFactory getSchemaFactory() { 150 if (schemaFactory == null) { 151 synchronized (this) { 152 if (schemaFactory == null) { 153 schemaFactory = createSchemaFactory(); 154 } 155 } 156 } 157 return schemaFactory; 158 } 159 160 public void setSchemaFactory(SchemaFactory schemaFactory) { 161 this.schemaFactory = schemaFactory; 162 } 163 164 public LSResourceResolver getResourceResolver() { 165 return resourceResolver; 166 } 167 168 public void setResourceResolver(LSResourceResolver resourceResolver) { 169 this.resourceResolver = resourceResolver; 170 } 171 172 protected SchemaFactory createSchemaFactory() { 173 SchemaFactory factory = SchemaFactory.newInstance(schemaLanguage); 174 if (getResourceResolver() != null) { 175 factory.setResourceResolver(getResourceResolver()); 176 } 177 if (camelContext == null || !Boolean.parseBoolean(camelContext.getGlobalOptions().get(ACCESS_EXTERNAL_DTD))) { 178 try { 179 LOG.debug("Configuring SchemaFactory to not allow access to external DTD/Schema"); 180 factory.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, ""); 181 } catch (SAXException e) { 182 LOG.warn(e.getMessage(), e); 183 } 184 } 185 return factory; 186 } 187 188 protected Source createSchemaSource() throws IOException { 189 throw new IllegalArgumentException("You must specify either a schema, schemaFile, schemaSource, schemaUrl, or schemaUri property"); 190 } 191 192 protected Schema createSchema() throws SAXException, IOException { 193 SchemaFactory factory = getSchemaFactory(); 194 195 URL url = getSchemaUrl(); 196 if (url != null) { 197 synchronized (this) { 198 return factory.newSchema(url); 199 } 200 } 201 202 File file = getSchemaFile(); 203 if (file != null) { 204 synchronized (this) { 205 return factory.newSchema(file); 206 } 207 } 208 209 byte[] bytes = getSchemaAsByteArray(); 210 if (bytes != null) { 211 synchronized (this) { 212 return factory.newSchema(new StreamSource(new ByteArrayInputStream(schemaAsByteArray))); 213 } 214 } 215 216 if (schemaResourceUri != null) { 217 synchronized (this) { 218 bytes = readSchemaResource(); 219 return factory.newSchema(new StreamSource(new ByteArrayInputStream(bytes))); 220 } 221 } 222 223 Source source = getSchemaSource(); 224 synchronized (this) { 225 return factory.newSchema(source); 226 } 227 228 } 229 230 protected byte[] readSchemaResource() throws IOException { 231 LOG.debug("reading schema resource: {}", schemaResourceUri); 232 InputStream is = ResourceHelper.resolveMandatoryResourceAsInputStream(camelContext, schemaResourceUri); 233 byte[] bytes = null; 234 try { 235 bytes = IOConverter.toBytes(is); 236 } finally { 237 // and make sure to close the input stream after the schema has been 238 // loaded 239 IOHelper.close(is); 240 } 241 return bytes; 242 } 243 244}