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