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(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 103 if (!schemaReaderConfigured) { 104 if (resourceResolver != null) { 105 schemaReader.setResourceResolver(resourceResolver); 106 } else if (resourceResolverFactory != null) { 107 resourceResolver = resourceResolverFactory.createResourceResolver(getCamelContext(), resourceUri); 108 // set the created resource resolver to the resourceResolver variable, so that it can 109 // be accessed by the endpoint 110 schemaReader.setResourceResolver(resourceResolver); 111 } else { 112 schemaReader.setResourceResolver(new DefaultValidatorResourceResolverFactory().createResourceResolver(getCamelContext(), resourceUri)); 113 } 114 schemaReader.setSchemaLanguage(getSchemaLanguage()); 115 schemaReader.setSchemaFactory(getSchemaFactory()); 116 117 // force loading of schema at create time otherwise concurrent 118 // processing could cause thread safe issues for the 119 // javax.xml.validation.SchemaFactory 120 schemaReader.loadSchema(); 121 122 // configure only once 123 schemaReaderConfigured = true; 124 } 125 126 ValidatingProcessor validator = new ValidatingProcessor(schemaReader); 127 configureValidator(validator); 128 129 return new ValidatorProducer(this, validator); 130 } 131 132 133 134 @Override 135 public Consumer createConsumer(Processor processor) throws Exception { 136 throw new UnsupportedOperationException("Cannot consume from validator"); 137 } 138 139 @Override 140 public boolean isSingleton() { 141 return true; 142 } 143 144 protected void configureValidator(ValidatingProcessor validator) throws Exception { 145 validator.setErrorHandler(getErrorHandler()); 146 validator.setUseDom(isUseDom()); 147 validator.setUseSharedSchema(isUseSharedSchema()); 148 validator.setFailOnNullBody(isFailOnNullBody()); 149 validator.setFailOnNullHeader(isFailOnNullHeader()); 150 validator.setHeaderName(getHeaderName()); 151 } 152 153 public String getResourceUri() { 154 return resourceUri; 155 } 156 157 /** 158 * URL to a local resource on the classpath,or a reference to lookup a bean in the Registry, 159 * or a full URL to a remote resource or resource on the file system which contains the XSD to validate against. 160 */ 161 public void setResourceUri(String resourceUri) { 162 this.resourceUri = resourceUri; 163 } 164 165 public String getSchemaLanguage() { 166 return schemaLanguage; 167 } 168 169 /** 170 * Configures the W3C XML Schema Namespace URI. 171 */ 172 public void setSchemaLanguage(String schemaLanguage) { 173 this.schemaLanguage = schemaLanguage; 174 } 175 176 public SchemaFactory getSchemaFactory() { 177 return schemaFactory; 178 } 179 180 /** 181 * To use a custom javax.xml.validation.SchemaFactory 182 */ 183 public void setSchemaFactory(SchemaFactory schemaFactory) { 184 this.schemaFactory = schemaFactory; 185 } 186 187 public ValidatorErrorHandler getErrorHandler() { 188 return errorHandler; 189 } 190 191 /** 192 * To use a custom org.apache.camel.processor.validation.ValidatorErrorHandler. 193 * <p/> 194 * The default error handler captures the errors and throws an exception. 195 */ 196 public void setErrorHandler(ValidatorErrorHandler errorHandler) { 197 this.errorHandler = errorHandler; 198 } 199 200 public boolean isUseDom() { 201 return useDom; 202 } 203 204 /** 205 * Whether DOMSource/DOMResult or SaxSource/SaxResult should be used by the validator. 206 */ 207 public void setUseDom(boolean useDom) { 208 this.useDom = useDom; 209 } 210 211 public boolean isUseSharedSchema() { 212 return useSharedSchema; 213 } 214 215 /** 216 * 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. 217 */ 218 public void setUseSharedSchema(boolean useSharedSchema) { 219 this.useSharedSchema = useSharedSchema; 220 } 221 222 public LSResourceResolver getResourceResolver() { 223 return resourceResolver; 224 } 225 226 /** 227 * To use a custom LSResourceResolver. See also {@link #setResourceResolverFactory(ValidatorResourceResolverFactory)} 228 */ 229 public void setResourceResolver(LSResourceResolver resourceResolver) { 230 this.resourceResolver = resourceResolver; 231 } 232 233 public ValidatorResourceResolverFactory getResourceResolverFactory() { 234 return resourceResolverFactory; 235 } 236 237 /** For creating a resource resolver which depends on the endpoint resource URI. 238 * Must not be used in combination with method {@link #setResourceResolver(LSResourceResolver). 239 * If not set then {@link DefaultValidatorResourceResolverFactory} is used 240 */ 241 public void setResourceResolverFactory(ValidatorResourceResolverFactory resourceResolverFactory) { 242 this.resourceResolverFactory = resourceResolverFactory; 243 } 244 245 public boolean isFailOnNullBody() { 246 return failOnNullBody; 247 } 248 249 /** 250 * Whether to fail if no body exists. 251 */ 252 public void setFailOnNullBody(boolean failOnNullBody) { 253 this.failOnNullBody = failOnNullBody; 254 } 255 256 public boolean isFailOnNullHeader() { 257 return failOnNullHeader; 258 } 259 260 /** 261 * Whether to fail if no header exists when validating against a header. 262 */ 263 public void setFailOnNullHeader(boolean failOnNullHeader) { 264 this.failOnNullHeader = failOnNullHeader; 265 } 266 267 public String getHeaderName() { 268 return headerName; 269 } 270 271 /** 272 * To validate against a header instead of the message body. 273 */ 274 public void setHeaderName(String headerName) { 275 this.headerName = headerName; 276 } 277}