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     */
017    package org.apache.camel.processor.validation;
018    
019    import java.io.File;
020    import java.io.IOException;
021    import java.net.URL;
022    import java.util.Collections;
023    
024    import javax.xml.XMLConstants;
025    import javax.xml.transform.Result;
026    import javax.xml.transform.Source;
027    import javax.xml.transform.dom.DOMResult;
028    import javax.xml.transform.dom.DOMSource;
029    import javax.xml.transform.sax.SAXResult;
030    import javax.xml.transform.sax.SAXSource;
031    import javax.xml.validation.Schema;
032    import javax.xml.validation.SchemaFactory;
033    import javax.xml.validation.Validator;
034    
035    import org.w3c.dom.ls.LSResourceResolver;
036    
037    import org.xml.sax.SAXException;
038    import org.xml.sax.SAXParseException;
039    
040    import org.apache.camel.Exchange;
041    import org.apache.camel.Processor;
042    
043    /**
044     * A processor which validates the XML version of the inbound message body
045     * against some schema either in XSD or RelaxNG
046     * 
047     * @version
048     */
049    public class ValidatingProcessor implements Processor {
050        // for lazy creation of the Schema
051        private String schemaLanguage = XMLConstants.W3C_XML_SCHEMA_NS_URI;
052        private Schema schema;
053        private Source schemaSource;
054        private SchemaFactory schemaFactory;
055        private URL schemaUrl;
056        private File schemaFile;
057        private ValidatorErrorHandler errorHandler = new DefaultValidationErrorHandler();
058        private boolean useDom;
059        private boolean useSharedSchema = true;
060        private LSResourceResolver resourceResolver;
061    
062        public void process(Exchange exchange) throws Exception {
063            Schema schema;
064            if (isUseSharedSchema()) {
065                schema = getSchema();
066            } else {
067                schema = createSchema();
068            }
069    
070            Validator validator = schema.newValidator();
071    
072            Source source;
073            Result result;
074            try {
075                if (useDom) {
076                    source = exchange.getIn().getBody(DOMSource.class);
077                    result = new DOMResult();
078                } else {
079                    source = exchange.getIn().getBody(SAXSource.class);
080                    result = new SAXResult();
081                }
082            } catch (Exception e) {
083                throw new NoXmlBodyValidationException(exchange, e);
084            }
085            if (source == null) {
086                throw new NoXmlBodyValidationException(exchange);
087            }
088    
089            // create a new errorHandler and set it on the validator
090            // must be a local instance to avoid problems with concurrency (to be
091            // thread safe)
092            ValidatorErrorHandler handler = errorHandler.getClass().newInstance();
093            validator.setErrorHandler(handler);
094    
095            try {
096                validator.validate(source, result);
097            } catch (SAXParseException e) {
098                // can be thrown for non well formed XML
099                throw new SchemaValidationException(exchange, schema, Collections.singletonList(e),
100                        Collections.<SAXParseException> emptyList(),
101                        Collections.<SAXParseException> emptyList());
102            }
103    
104            handler.handleErrors(exchange, schema, result);
105        }
106    
107        public void loadSchema() throws Exception {
108            // force loading of schema
109            schema = createSchema();
110        }
111    
112        // Properties
113        // -----------------------------------------------------------------------
114    
115        public Schema getSchema() throws IOException, SAXException {
116            if (schema == null) {
117                schema = createSchema();
118            }
119            return schema;
120        }
121    
122        public void setSchema(Schema schema) {
123            this.schema = schema;
124        }
125    
126        public String getSchemaLanguage() {
127            return schemaLanguage;
128        }
129    
130        public void setSchemaLanguage(String schemaLanguage) {
131            this.schemaLanguage = schemaLanguage;
132        }
133    
134        public Source getSchemaSource() throws IOException {
135            if (schemaSource == null) {
136                schemaSource = createSchemaSource();
137            }
138            return schemaSource;
139        }
140    
141        public void setSchemaSource(Source schemaSource) {
142            this.schemaSource = schemaSource;
143        }
144    
145        public URL getSchemaUrl() {
146            return schemaUrl;
147        }
148    
149        public void setSchemaUrl(URL schemaUrl) {
150            this.schemaUrl = schemaUrl;
151        }
152    
153        public File getSchemaFile() {
154            return schemaFile;
155        }
156    
157        public void setSchemaFile(File schemaFile) {
158            this.schemaFile = schemaFile;
159        }
160    
161        public SchemaFactory getSchemaFactory() {
162            if (schemaFactory == null) {
163                schemaFactory = createSchemaFactory();
164            }
165            return schemaFactory;
166        }
167    
168        public void setSchemaFactory(SchemaFactory schemaFactory) {
169            this.schemaFactory = schemaFactory;
170        }
171    
172        public ValidatorErrorHandler getErrorHandler() {
173            return errorHandler;
174        }
175    
176        public void setErrorHandler(ValidatorErrorHandler errorHandler) {
177            this.errorHandler = errorHandler;
178        }
179    
180        public boolean isUseDom() {
181            return useDom;
182        }
183    
184        /**
185         * Sets whether DOMSource and DOMResult should be used, or SaxSource and
186         * SaxResult.
187         * 
188         * @param useDom
189         *            true to use DOM otherwise Sax is used
190         */
191        public void setUseDom(boolean useDom) {
192            this.useDom = useDom;
193        }
194    
195        public boolean isUseSharedSchema() {
196            return useSharedSchema;
197        }
198    
199        public void setUseSharedSchema(boolean useSharedSchema) {
200            this.useSharedSchema = useSharedSchema;
201        }
202    
203        public LSResourceResolver getResourceResolver() {
204            return resourceResolver;
205        }
206    
207        public void setResourceResolver(LSResourceResolver resourceResolver) {
208            this.resourceResolver = resourceResolver;
209        }
210    
211        // Implementation methods
212        // -----------------------------------------------------------------------
213    
214        protected SchemaFactory createSchemaFactory() {
215            SchemaFactory factory = SchemaFactory.newInstance(schemaLanguage);
216            if (getResourceResolver() != null) {
217                factory.setResourceResolver(getResourceResolver());
218            }
219            return factory;
220        }
221    
222        protected Source createSchemaSource() throws IOException {
223            throw new IllegalArgumentException("You must specify either a schema, schemaFile, schemaSource or schemaUrl property");
224        }
225    
226        protected Schema createSchema() throws SAXException, IOException {
227            SchemaFactory factory = getSchemaFactory();
228    
229            URL url = getSchemaUrl();
230            if (url != null) {
231                return factory.newSchema(url);
232            }
233            File file = getSchemaFile();
234            if (file != null) {
235                return factory.newSchema(file);
236            }
237            return factory.newSchema(getSchemaSource());
238        }
239    }