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