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