/** * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.camel.processor.validation; import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.URL; import javax.xml.XMLConstants; import javax.xml.transform.Source; import javax.xml.transform.stream.StreamSource; import javax.xml.validation.Schema; import javax.xml.validation.SchemaFactory; import org.w3c.dom.ls.LSResourceResolver; import org.xml.sax.SAXException; import org.apache.camel.CamelContext; import org.apache.camel.converter.IOConverter; import org.apache.camel.util.IOHelper; import org.apache.camel.util.ObjectHelper; import org.apache.camel.util.ResourceHelper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Reads the schema used in the processor {@link ValidatingProcessor}. * A schema re-reading could be forced using {@link org.apache.camel.component.validator.ValidatorEndpoint#clearCachedSchema()}. */ public class SchemaReader { /** Key of the global option to switch either off or on the access to external DTDs in the XML Validator for StreamSources. * Only effective, if not a custom schema factory is used.*/ public static final String ACCESS_EXTERNAL_DTD = "CamelXmlValidatorAccessExternalDTD"; private static final Logger LOG = LoggerFactory.getLogger(SchemaReader.class); private String schemaLanguage = XMLConstants.W3C_XML_SCHEMA_NS_URI; // must be volatile because is accessed from different threads see ValidatorEndpoint.clearCachedSchema private volatile Schema schema; private Source schemaSource; // must be volatile because is accessed from different threads see ValidatorEndpoint.clearCachedSchema private volatile SchemaFactory schemaFactory; private URL schemaUrl; private File schemaFile; private byte[] schemaAsByteArray; private final String schemaResourceUri; private LSResourceResolver resourceResolver; private final CamelContext camelContext; public SchemaReader() { this.camelContext = null; this.schemaResourceUri = null; } /** Specify a camel context and a schema resource URI in order to read the schema via the class resolver specified in the Camel context. */ public SchemaReader(CamelContext camelContext, String schemaResourceUri) { ObjectHelper.notNull(camelContext, "camelContext"); ObjectHelper.notNull(schemaResourceUri, "schemaResourceUri"); this.camelContext = camelContext; this.schemaResourceUri = schemaResourceUri; } public void loadSchema() throws Exception { // force loading of schema schema = createSchema(); } // Properties // ----------------------------------------------------------------------- public Schema getSchema() throws IOException, SAXException { if (schema == null) { synchronized (this) { if (schema == null) { schema = createSchema(); } } } return schema; } public void setSchema(Schema schema) { this.schema = schema; } public String getSchemaLanguage() { return schemaLanguage; } public void setSchemaLanguage(String schemaLanguage) { this.schemaLanguage = schemaLanguage; } public Source getSchemaSource() throws IOException { if (schemaSource == null) { schemaSource = createSchemaSource(); } return schemaSource; } public void setSchemaSource(Source schemaSource) { this.schemaSource = schemaSource; } public URL getSchemaUrl() { return schemaUrl; } public void setSchemaUrl(URL schemaUrl) { this.schemaUrl = schemaUrl; } public File getSchemaFile() { return schemaFile; } public void setSchemaFile(File schemaFile) { this.schemaFile = schemaFile; } public byte[] getSchemaAsByteArray() { return schemaAsByteArray; } public void setSchemaAsByteArray(byte[] schemaAsByteArray) { this.schemaAsByteArray = schemaAsByteArray; } public SchemaFactory getSchemaFactory() { if (schemaFactory == null) { synchronized (this) { if (schemaFactory == null) { schemaFactory = createSchemaFactory(); } } } return schemaFactory; } public void setSchemaFactory(SchemaFactory schemaFactory) { this.schemaFactory = schemaFactory; } public LSResourceResolver getResourceResolver() { return resourceResolver; } public void setResourceResolver(LSResourceResolver resourceResolver) { this.resourceResolver = resourceResolver; } protected SchemaFactory createSchemaFactory() { SchemaFactory factory = SchemaFactory.newInstance(schemaLanguage); if (getResourceResolver() != null) { factory.setResourceResolver(getResourceResolver()); } if (camelContext == null || !Boolean.parseBoolean(camelContext.getGlobalOptions().get(ACCESS_EXTERNAL_DTD))) { try { factory.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, ""); } catch (SAXException e) { LOG.warn(e.getMessage(), e); } } return factory; } protected Source createSchemaSource() throws IOException { throw new IllegalArgumentException("You must specify either a schema, schemaFile, schemaSource, schemaUrl, or schemaUri property"); } protected Schema createSchema() throws SAXException, IOException { SchemaFactory factory = getSchemaFactory(); URL url = getSchemaUrl(); if (url != null) { synchronized (this) { return factory.newSchema(url); } } File file = getSchemaFile(); if (file != null) { synchronized (this) { return factory.newSchema(file); } } byte[] bytes = getSchemaAsByteArray(); if (bytes != null) { synchronized (this) { return factory.newSchema(new StreamSource(new ByteArrayInputStream(schemaAsByteArray))); } } if (schemaResourceUri != null) { synchronized (this) { bytes = readSchemaResource(); return factory.newSchema(new StreamSource(new ByteArrayInputStream(bytes))); } } Source source = getSchemaSource(); synchronized (this) { return factory.newSchema(source); } } protected byte[] readSchemaResource() throws IOException { LOG.debug("reading schema resource: {}", schemaResourceUri); InputStream is = ResourceHelper.resolveMandatoryResourceAsInputStream(camelContext, schemaResourceUri); byte[] bytes = null; try { bytes = IOConverter.toBytes(is); } finally { // and make sure to close the input stream after the schema has been // loaded IOHelper.close(is); } return bytes; } }