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.component.validator;
018
019import java.io.InputStream;
020import javax.xml.XMLConstants;
021import javax.xml.validation.SchemaFactory;
022
023import org.w3c.dom.ls.LSResourceResolver;
024
025import org.apache.camel.Component;
026import org.apache.camel.Consumer;
027import org.apache.camel.Processor;
028import org.apache.camel.Producer;
029import org.apache.camel.converter.IOConverter;
030import org.apache.camel.impl.DefaultEndpoint;
031import org.apache.camel.processor.validation.DefaultValidationErrorHandler;
032import org.apache.camel.processor.validation.ValidatingProcessor;
033import org.apache.camel.processor.validation.ValidatorErrorHandler;
034import org.apache.camel.spi.Metadata;
035import org.apache.camel.spi.UriEndpoint;
036import org.apache.camel.spi.UriParam;
037import org.apache.camel.spi.UriPath;
038import org.apache.camel.util.IOHelper;
039import org.apache.camel.util.ResourceHelper;
040import org.slf4j.Logger;
041import org.slf4j.LoggerFactory;
042
043@UriEndpoint(scheme = "validator", title = "Validator", syntax = "validator:resourceUri", producerOnly = true, label = "core,validation")
044public class ValidatorEndpoint extends DefaultEndpoint {
045
046    private static final Logger LOG = LoggerFactory.getLogger(ValidatorEndpoint.class);
047
048    @UriPath(description = "URL to a local resource on the classpath or a full URL to a remote resource or resource on the file system which contains the XSD to validate against.")
049    @Metadata(required = "true")
050    private String resourceUri;
051    @UriParam(defaultValue = XMLConstants.W3C_XML_SCHEMA_NS_URI, description = "Configures the W3C XML Schema Namespace URI.")
052    private String schemaLanguage = XMLConstants.W3C_XML_SCHEMA_NS_URI;
053    @UriParam(description = "To use a custom javax.xml.validation.SchemaFactory")
054    private SchemaFactory schemaFactory;
055    @UriParam(description = "To use a custom org.apache.camel.processor.validation.ValidatorErrorHandler. The default error handler captures the errors and throws an exception.")
056    private ValidatorErrorHandler errorHandler = new DefaultValidationErrorHandler();
057    @UriParam(description = "Whether DOMSource/DOMResult or SaxSource/SaxResult should be used by the validator.")
058    private boolean useDom;
059    @UriParam(defaultValue = "true", description = "Whether the Schema instance should be shared or not. This option is introduced to work around a JDK 1.6.x bug. Xerces should not have this issue.")
060    private boolean useSharedSchema = true;
061    @UriParam(description = "To use a custom LSResourceResolver")
062    private LSResourceResolver resourceResolver;
063    @UriParam(defaultValue = "true", description = "Whether to fail if no body exists.")
064    private boolean failOnNullBody = true;
065    @UriParam(defaultValue = "true", description = "Whether to fail if no header exists when validating against a header.")
066    private boolean failOnNullHeader = true;
067    @UriParam(description = "To validate against a header instead of the message body.")
068    private String headerName;
069
070    public ValidatorEndpoint() {
071    }
072
073    public ValidatorEndpoint(String endpointUri, Component component, String resourceUri) {
074        super(endpointUri, component);
075        this.resourceUri = resourceUri;
076    }
077
078    @Override
079    public Producer createProducer() throws Exception {
080        ValidatingProcessor validator = new ValidatingProcessor();
081
082        InputStream is = ResourceHelper.resolveMandatoryResourceAsInputStream(getCamelContext().getClassResolver(), resourceUri);
083        byte[] bytes = null;
084        try {
085            bytes = IOConverter.toBytes(is);
086        } finally {
087            // and make sure to close the input stream after the schema has been loaded
088            IOHelper.close(is);
089        }
090
091        validator.setSchemaAsByteArray(bytes);
092        LOG.debug("{} using schema resource: {}", this, resourceUri);
093        configureValidator(validator);
094
095        // force loading of schema at create time otherwise concurrent
096        // processing could cause thread safe issues for the javax.xml.validation.SchemaFactory
097        validator.loadSchema();
098
099        return new ValidatorProducer(this, validator);
100    }
101
102    @Override
103    public Consumer createConsumer(Processor processor) throws Exception {
104        throw new UnsupportedOperationException("Cannot consume from validator");
105    }
106
107    @Override
108    public boolean isSingleton() {
109        return true;
110    }
111
112    protected void configureValidator(ValidatingProcessor validator) throws Exception {
113        if (resourceResolver != null) {
114            validator.setResourceResolver(resourceResolver);
115        } else {
116            validator.setResourceResolver(new DefaultLSResourceResolver(getCamelContext(), resourceUri));
117        }
118        validator.setSchemaLanguage(getSchemaLanguage());
119        validator.setSchemaFactory(getSchemaFactory());
120        validator.setErrorHandler(getErrorHandler());
121        validator.setUseDom(isUseDom());
122        validator.setUseSharedSchema(isUseSharedSchema());
123        validator.setFailOnNullBody(isFailOnNullBody());
124        validator.setFailOnNullHeader(isFailOnNullHeader());
125        validator.setHeaderName(getHeaderName());
126    }
127
128    public String getResourceUri() {
129        return resourceUri;
130    }
131
132    /**
133     * URL to a local resource on the classpath or a full URL to a remote resource or resource on the file system which contains the XSD to validate against.
134     */
135    public void setResourceUri(String resourceUri) {
136        this.resourceUri = resourceUri;
137    }
138
139    public String getSchemaLanguage() {
140        return schemaLanguage;
141    }
142
143    /**
144     * Configures the W3C XML Schema Namespace URI.
145     */
146    public void setSchemaLanguage(String schemaLanguage) {
147        this.schemaLanguage = schemaLanguage;
148    }
149
150    public SchemaFactory getSchemaFactory() {
151        return schemaFactory;
152    }
153
154    /**
155     * To use a custom javax.xml.validation.SchemaFactory
156     */
157    public void setSchemaFactory(SchemaFactory schemaFactory) {
158        this.schemaFactory = schemaFactory;
159    }
160
161    public ValidatorErrorHandler getErrorHandler() {
162        return errorHandler;
163    }
164
165    /**
166     * To use a custom org.apache.camel.processor.validation.ValidatorErrorHandler.
167     * <p/>
168     * The default error handler captures the errors and throws an exception.
169     */
170    public void setErrorHandler(ValidatorErrorHandler errorHandler) {
171        this.errorHandler = errorHandler;
172    }
173
174    public boolean isUseDom() {
175        return useDom;
176    }
177
178    /**
179     * Whether DOMSource/DOMResult or SaxSource/SaxResult should be used by the validator.
180     */
181    public void setUseDom(boolean useDom) {
182        this.useDom = useDom;
183    }
184
185    public boolean isUseSharedSchema() {
186        return useSharedSchema;
187    }
188
189    /**
190     * Whether the Schema instance should be shared or not. This option is introduced to work around a JDK 1.6.x bug. Xerces should not have this issue.
191     */
192    public void setUseSharedSchema(boolean useSharedSchema) {
193        this.useSharedSchema = useSharedSchema;
194    }
195
196    public LSResourceResolver getResourceResolver() {
197        return resourceResolver;
198    }
199
200    /**
201     * To use a custom LSResourceResolver
202     */
203    public void setResourceResolver(LSResourceResolver resourceResolver) {
204        this.resourceResolver = resourceResolver;
205    }
206
207    public boolean isFailOnNullBody() {
208        return failOnNullBody;
209    }
210
211    /**
212     * Whether to fail if no body exists.
213     */
214    public void setFailOnNullBody(boolean failOnNullBody) {
215        this.failOnNullBody = failOnNullBody;
216    }
217
218    public boolean isFailOnNullHeader() {
219        return failOnNullHeader;
220    }
221
222    /**
223     * Whether to fail if no header exists when validating against a header.
224     */
225    public void setFailOnNullHeader(boolean failOnNullHeader) {
226        this.failOnNullHeader = failOnNullHeader;
227    }
228
229    public String getHeaderName() {
230        return headerName;
231    }
232
233    /**
234     * To validate against a header instead of the message body.
235     */
236    public void setHeaderName(String headerName) {
237        this.headerName = headerName;
238    }
239}