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