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 javax.xml.XMLConstants; 020import javax.xml.validation.SchemaFactory; 021 022import org.w3c.dom.ls.LSResourceResolver; 023 024import org.apache.camel.Component; 025import org.apache.camel.Consumer; 026import org.apache.camel.Processor; 027import org.apache.camel.Producer; 028import org.apache.camel.api.management.ManagedOperation; 029import org.apache.camel.api.management.ManagedResource; 030import org.apache.camel.impl.DefaultEndpoint; 031import org.apache.camel.processor.validation.DefaultValidationErrorHandler; 032import org.apache.camel.processor.validation.SchemaReader; 033import org.apache.camel.processor.validation.ValidatingProcessor; 034import org.apache.camel.processor.validation.ValidatorErrorHandler; 035import org.apache.camel.spi.Metadata; 036import org.apache.camel.spi.UriEndpoint; 037import org.apache.camel.spi.UriParam; 038import org.apache.camel.spi.UriPath; 039 040 041/** 042 * Validates the payload of a message using XML Schema and JAXP Validation. 043 */ 044@ManagedResource(description = "Managed ValidatorEndpoint") 045@UriEndpoint(firstVersion = "1.1.0", scheme = "validator", title = "Validator", syntax = "validator:resourceUri", producerOnly = true, label = "core,validation") 046public class ValidatorEndpoint extends DefaultEndpoint { 047 048 @UriPath(description = "URL to a local resource on the classpath, or a reference to lookup a bean in the Registry," 049 + " or a full URL to a remote resource or resource on the file system which contains the XSD to validate against.") 050 @Metadata(required = "true") 051 private String resourceUri; 052 @UriParam(defaultValue = XMLConstants.W3C_XML_SCHEMA_NS_URI, label = "advanced", 053 description = "Configures the W3C XML Schema Namespace URI.") 054 private String schemaLanguage = XMLConstants.W3C_XML_SCHEMA_NS_URI; 055 @UriParam(label = "advanced", description = "To use a custom javax.xml.validation.SchemaFactory") 056 private SchemaFactory schemaFactory; 057 @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.") 058 private ValidatorErrorHandler errorHandler = new DefaultValidationErrorHandler(); 059 @UriParam(label = "advanced", description = "Whether DOMSource/DOMResult or SaxSource/SaxResult should be used by the validator.") 060 private boolean useDom; 061 @UriParam(defaultValue = "true", label = "advanced", 062 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.") 063 private boolean useSharedSchema = true; 064 @UriParam(label = "advanced", description = "To use a custom LSResourceResolver. Do not use together with resourceResolverFactory") 065 private LSResourceResolver resourceResolver; 066 @UriParam(label = "advanced", description = "To use a custom LSResourceResolver which depends on a dynamic endpoint resource URI. " + // 067 "The default resource resolver factory resturns a resource resolver which can read files from the class path and file system. Do not use together with resourceResolver.") 068 private ValidatorResourceResolverFactory resourceResolverFactory; 069 @UriParam(defaultValue = "true", description = "Whether to fail if no body exists.") 070 private boolean failOnNullBody = true; 071 @UriParam(defaultValue = "true", description = "Whether to fail if no header exists when validating against a header.") 072 private boolean failOnNullHeader = true; 073 @UriParam(description = "To validate against a header instead of the message body.") 074 private String headerName; 075 076 /** 077 * We need a one-to-one relation between endpoint and schema reader in order 078 * to be able to clear the cached schema in the schema reader. See method 079 * {@link #clearCachedSchema}. 080 */ 081 private final SchemaReader schemaReader; 082 private volatile boolean schemaReaderConfigured; 083 084 public ValidatorEndpoint() { 085 schemaReader = new SchemaReader(); 086 } 087 088 public ValidatorEndpoint(String endpointUri, Component component, String resourceUri) { 089 super(endpointUri, component); 090 this.resourceUri = resourceUri; 091 schemaReader = new SchemaReader(getCamelContext(), resourceUri); 092 } 093 094 @ManagedOperation(description = "Clears the cached schema, forcing to re-load the schema on next request") 095 public void clearCachedSchema() { 096 097 schemaReader.setSchema(null); // will cause to reload the schema 098 } 099 100 @Override 101 public Producer createProducer() throws Exception { 102 if (!schemaReaderConfigured) { 103 if (resourceResolver != null) { 104 schemaReader.setResourceResolver(resourceResolver); 105 } else if (resourceResolverFactory != null) { 106 resourceResolver = resourceResolverFactory.createResourceResolver(getCamelContext(), resourceUri); 107 // set the created resource resolver to the resourceResolver variable, so that it can 108 // be accessed by the endpoint 109 schemaReader.setResourceResolver(resourceResolver); 110 } else { 111 schemaReader.setResourceResolver(new DefaultValidatorResourceResolverFactory().createResourceResolver(getCamelContext(), resourceUri)); 112 } 113 schemaReader.setSchemaLanguage(getSchemaLanguage()); 114 schemaReader.setSchemaFactory(getSchemaFactory()); 115 116 // force loading of schema at create time otherwise concurrent 117 // processing could cause thread safe issues for the 118 // javax.xml.validation.SchemaFactory 119 schemaReader.loadSchema(); 120 121 // configure only once 122 schemaReaderConfigured = true; 123 } 124 125 ValidatingProcessor validator = new ValidatingProcessor(schemaReader); 126 configureValidator(validator); 127 128 return new ValidatorProducer(this, validator); 129 } 130 131 @Override 132 public Consumer createConsumer(Processor processor) throws Exception { 133 throw new UnsupportedOperationException("Cannot consume from validator"); 134 } 135 136 @Override 137 public boolean isSingleton() { 138 return true; 139 } 140 141 protected void configureValidator(ValidatingProcessor validator) throws Exception { 142 validator.setErrorHandler(getErrorHandler()); 143 validator.setUseDom(isUseDom()); 144 validator.setUseSharedSchema(isUseSharedSchema()); 145 validator.setFailOnNullBody(isFailOnNullBody()); 146 validator.setFailOnNullHeader(isFailOnNullHeader()); 147 validator.setHeaderName(getHeaderName()); 148 } 149 150 public String getResourceUri() { 151 return resourceUri; 152 } 153 154 /** 155 * URL to a local resource on the classpath,or a reference to lookup a bean in the Registry, 156 * or a full URL to a remote resource or resource on the file system which contains the XSD to validate against. 157 */ 158 public void setResourceUri(String resourceUri) { 159 this.resourceUri = resourceUri; 160 } 161 162 public String getSchemaLanguage() { 163 return schemaLanguage; 164 } 165 166 /** 167 * Configures the W3C XML Schema Namespace URI. 168 */ 169 public void setSchemaLanguage(String schemaLanguage) { 170 this.schemaLanguage = schemaLanguage; 171 } 172 173 public SchemaFactory getSchemaFactory() { 174 return schemaFactory; 175 } 176 177 /** 178 * To use a custom javax.xml.validation.SchemaFactory 179 */ 180 public void setSchemaFactory(SchemaFactory schemaFactory) { 181 this.schemaFactory = schemaFactory; 182 } 183 184 public ValidatorErrorHandler getErrorHandler() { 185 return errorHandler; 186 } 187 188 /** 189 * To use a custom org.apache.camel.processor.validation.ValidatorErrorHandler. 190 * <p/> 191 * The default error handler captures the errors and throws an exception. 192 */ 193 public void setErrorHandler(ValidatorErrorHandler errorHandler) { 194 this.errorHandler = errorHandler; 195 } 196 197 public boolean isUseDom() { 198 return useDom; 199 } 200 201 /** 202 * Whether DOMSource/DOMResult or SaxSource/SaxResult should be used by the validator. 203 */ 204 public void setUseDom(boolean useDom) { 205 this.useDom = useDom; 206 } 207 208 public boolean isUseSharedSchema() { 209 return useSharedSchema; 210 } 211 212 /** 213 * 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. 214 */ 215 public void setUseSharedSchema(boolean useSharedSchema) { 216 this.useSharedSchema = useSharedSchema; 217 } 218 219 public LSResourceResolver getResourceResolver() { 220 return resourceResolver; 221 } 222 223 /** 224 * To use a custom LSResourceResolver. See also {@link #setResourceResolverFactory(ValidatorResourceResolverFactory)} 225 */ 226 public void setResourceResolver(LSResourceResolver resourceResolver) { 227 this.resourceResolver = resourceResolver; 228 } 229 230 public ValidatorResourceResolverFactory getResourceResolverFactory() { 231 return resourceResolverFactory; 232 } 233 234 /** For creating a resource resolver which depends on the endpoint resource URI. 235 * Must not be used in combination with method {@link #setResourceResolver(LSResourceResolver)}. 236 * If not set then {@link DefaultValidatorResourceResolverFactory} is used 237 */ 238 public void setResourceResolverFactory(ValidatorResourceResolverFactory resourceResolverFactory) { 239 this.resourceResolverFactory = resourceResolverFactory; 240 } 241 242 public boolean isFailOnNullBody() { 243 return failOnNullBody; 244 } 245 246 /** 247 * Whether to fail if no body exists. 248 */ 249 public void setFailOnNullBody(boolean failOnNullBody) { 250 this.failOnNullBody = failOnNullBody; 251 } 252 253 public boolean isFailOnNullHeader() { 254 return failOnNullHeader; 255 } 256 257 /** 258 * Whether to fail if no header exists when validating against a header. 259 */ 260 public void setFailOnNullHeader(boolean failOnNullHeader) { 261 this.failOnNullHeader = failOnNullHeader; 262 } 263 264 public String getHeaderName() { 265 return headerName; 266 } 267 268 /** 269 * To validate against a header instead of the message body. 270 */ 271 public void setHeaderName(String headerName) { 272 this.headerName = headerName; 273 } 274}