/*
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.integration.xml.selector;
import java.io.IOException;
import java.util.Arrays;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.xml.sax.SAXParseException;
import org.springframework.core.io.Resource;
import org.springframework.integration.MessageRejectedException;
import org.springframework.integration.core.MessageSelector;
import org.springframework.integration.xml.AggregatedXmlMessageValidationException;
import org.springframework.integration.xml.DefaultXmlPayloadConverter;
import org.springframework.integration.xml.XmlPayloadConverter;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHandlingException;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springframework.xml.validation.XmlValidator;
import org.springframework.xml.validation.XmlValidatorFactory;
/**
* @author Oleg Zhurakousky
* @author Gary Russell
* @author Liujiong
* @since 2.0
*/
public class XmlValidatingMessageSelector implements MessageSelector {
public enum SchemaType {
XML_SCHEMA(XmlValidatorFactory.SCHEMA_W3C_XML),
RELAX_NG(XmlValidatorFactory.SCHEMA_RELAX_NG);
private final String url;
SchemaType(String url) {
this.url = url;
}
public String getUrl() {
return this.url;
}
}
private final Log logger = LogFactory.getLog(this.getClass());
private final XmlValidator xmlValidator;
private volatile boolean throwExceptionOnRejection;
private volatile XmlPayloadConverter converter = new DefaultXmlPayloadConverter();
public XmlValidatingMessageSelector(XmlValidator xmlValidator) {
Assert.notNull(xmlValidator, "XmlValidator must not be null");
this.xmlValidator = xmlValidator;
}
/**
* Creates a selector with a default {@link XmlValidator}. The validator will be initialized with
* the provided 'schema' location {@link Resource} and 'schemaType'. The valid options for schema
* type are {@link XmlValidatorFactory#SCHEMA_W3C_XML} or {@link XmlValidatorFactory#SCHEMA_RELAX_NG}.
* If no 'schemaType' is provided it will default to {@link XmlValidatorFactory#SCHEMA_W3C_XML};
*
* @param schema The schema.
* @param schemaType The schema type.
*
* @throws IOException if the XmlValidatorFactory fails to create a validator
*/
public XmlValidatingMessageSelector(Resource schema, SchemaType schemaType) throws IOException {
Assert.notNull(schema, "You must provide XML schema location to perform validation");
if (schemaType == null) {
schemaType = SchemaType.XML_SCHEMA;
}
this.xmlValidator = XmlValidatorFactory.createValidator(schema, schemaType.getUrl());
}
public XmlValidatingMessageSelector(Resource schema, String schemaType) throws IOException {
this(schema, StringUtils.isEmpty(schemaType) ? null :
SchemaType.valueOf(schemaType.toUpperCase().replaceFirst("-", "_")));
}
public void setThrowExceptionOnRejection(boolean throwExceptionOnRejection) {
this.throwExceptionOnRejection = throwExceptionOnRejection;
}
/**
* Specify the Converter to use when converting payloads prior to validation.
*
* @param converter The payload converter.
*/
public void setConverter(XmlPayloadConverter converter) {
Assert.notNull(converter, "'converter' must not be null");
this.converter = converter;
}
@Override
public boolean accept(Message<?> message) {
SAXParseException[] validationExceptions = null;
try {
validationExceptions = this.xmlValidator.validate(this.converter.convertToSource(message.getPayload()));
}
catch (Exception e) {
throw new MessageHandlingException(message, e);
}
boolean validationSuccess = ObjectUtils.isEmpty(validationExceptions);
if (!validationSuccess) {
if (this.throwExceptionOnRejection) {
throw new MessageRejectedException(message, "Message was rejected due to XML Validation errors",
new AggregatedXmlMessageValidationException(
Arrays.<Throwable>asList(validationExceptions)));
}
this.logger.debug("Message was rejected due to XML Validation errors");
}
return validationSuccess;
}
}