Allow XML components injection

**Cherry-pick to 5.0.x & 4.3.x**

* Polishing after rebase
* Copyright to 2019

* Rebase and update according upstream deps
* Do not rely on `spring-web` in classpath: use `ClassUtils.forName()`
to load `ServletContextResource` class for checking

# Conflicts:
#	build.gradle
#	spring-integration-xml/src/main/java/org/springframework/integration/xml/selector/XmlValidatingMessageSelector.java
#	spring-integration-xml/src/main/java/org/springframework/integration/xml/transformer/XsltPayloadTransformer.java
#	spring-integration-xml/src/main/java/org/springframework/integration/xml/xpath/XPathUtils.java
#	spring-integration-xml/src/test/java/org/springframework/integration/xml/transformer/XsltPayloadTransformerTests.java

# Conflicts:
#	build.gradle
#	spring-integration-xml/src/main/java/org/springframework/integration/xml/DefaultXmlPayloadConverter.java
#	spring-integration-xml/src/main/java/org/springframework/integration/xml/splitter/XPathMessageSplitter.java
#	spring-integration-xml/src/test/java/org/springframework/integration/xml/transformer/XsltPayloadTransformerTests.java
This commit is contained in:
Artem Bilan
2018-12-10 18:43:15 -05:00
parent a2d419ef25
commit b720140faf
17 changed files with 393 additions and 198 deletions

View File

@@ -138,7 +138,7 @@ subprojects { subproject ->
springSocialTwitterVersion = '1.1.2.RELEASE'
springRetryVersion = '1.1.3.RELEASE'
springVersion = project.hasProperty('springVersion') ? project.springVersion : '4.3.22.RELEASE'
springWsVersion = '2.3.0.RELEASE'
springWsVersion = '2.4.4.RELEASE'
xmlUnitVersion = '1.6'
xstreamVersion = '1.4.7'
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2019 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.
@@ -33,6 +33,8 @@ import org.w3c.dom.Node;
import org.xml.sax.InputSource;
import org.springframework.messaging.MessagingException;
import org.springframework.util.Assert;
import org.springframework.xml.DocumentBuilderFactoryUtils;
import org.springframework.xml.transform.StringSource;
/**
@@ -49,11 +51,12 @@ public class DefaultXmlPayloadConverter implements XmlPayloadConverter {
public DefaultXmlPayloadConverter() {
this.documentBuilderFactory = DocumentBuilderFactory.newInstance();
this(DocumentBuilderFactoryUtils.newInstance());
this.documentBuilderFactory.setNamespaceAware(true);
}
public DefaultXmlPayloadConverter(DocumentBuilderFactory documentBuilderFactory) {
Assert.notNull(documentBuilderFactory, "'documentBuilderFactory' must not be null.");
this.documentBuilderFactory = documentBuilderFactory;
}
@@ -116,7 +119,7 @@ public class DefaultXmlPayloadConverter implements XmlPayloadConverter {
@Override
public Node convertToNode(Object object) {
Node node = null;
Node node;
if (object instanceof Node) {
node = (Node) object;
}
@@ -131,7 +134,7 @@ public class DefaultXmlPayloadConverter implements XmlPayloadConverter {
@Override
public Source convertToSource(Object object) {
Source source = null;
Source source;
if (object instanceof Source) {
source = (Source) object;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2014 the original author or authors.
* Copyright 2002-2019 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.
@@ -34,6 +34,7 @@ import org.springframework.util.StringUtils;
*
* @author Mark Fisher
* @author Artem Bilan
*
* @since 2.0
*/
public class XPathHeaderEnricherParser extends AbstractTransformerParser {
@@ -46,7 +47,7 @@ public class XPathHeaderEnricherParser extends AbstractTransformerParser {
@Override
protected void parseTransformer(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
ManagedMap<String, Object> headers = new ManagedMap<String, Object>();
this.processHeaders(element, headers, parserContext);
processHeaders(element, headers, parserContext);
builder.addConstructorArgValue(headers);
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "default-overwrite");
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "should-skip-nulls");
@@ -54,6 +55,7 @@ public class XPathHeaderEnricherParser extends AbstractTransformerParser {
protected void processHeaders(Element element, ManagedMap<String, Object> headers, ParserContext parserContext) {
Object source = parserContext.extractSource(element);
String converter = element.getAttribute("converter");
NodeList childNodes = element.getChildNodes();
for (int i = 0; i < childNodes.getLength(); i++) {
Node node = childNodes.item(i);
@@ -62,14 +64,16 @@ public class XPathHeaderEnricherParser extends AbstractTransformerParser {
String elementName = node.getLocalName();
if ("header".equals(elementName)) {
BeanDefinitionBuilder builder =
BeanDefinitionBuilder.genericBeanDefinition(XPathExpressionEvaluatingHeaderValueMessageProcessor.class);
BeanDefinitionBuilder.genericBeanDefinition(
XPathExpressionEvaluatingHeaderValueMessageProcessor.class);
String expressionString = headerElement.getAttribute("xpath-expression");
String expressionRef = headerElement.getAttribute("xpath-expression-ref");
boolean isExpressionString = StringUtils.hasText(expressionString);
boolean isExpressionRef = StringUtils.hasText(expressionRef);
if (!(isExpressionString ^ isExpressionRef)) {
if (isExpressionString == isExpressionRef) {
parserContext.getReaderContext().error(
"Exactly one of the 'xpath-expression' or 'xpath-expression-ref' attributes is required.", source);
"Exactly one of the 'xpath-expression' " +
"or 'xpath-expression-ref' attributes is required.", source);
}
if (isExpressionString) {
builder.addConstructorArgValue(expressionString);
@@ -77,6 +81,9 @@ public class XPathHeaderEnricherParser extends AbstractTransformerParser {
else {
builder.addConstructorArgReference(expressionRef);
}
if (StringUtils.hasText(converter)) {
builder.addConstructorArgReference(converter);
}
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, headerElement, "evaluation-type");
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, headerElement, "overwrite");
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, headerElement, "header-type");

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2010 the original author or authors.
* Copyright 2002-2019 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.
@@ -23,23 +23,26 @@ import javax.xml.transform.Result;
import javax.xml.transform.dom.DOMResult;
import org.springframework.messaging.MessagingException;
import org.springframework.util.Assert;
import org.springframework.xml.DocumentBuilderFactoryUtils;
/**
* @author Jonas Partner
* @author Artem Bilan
*/
public class DomResultFactory implements ResultFactory {
private final DocumentBuilderFactory documentBuilderFactory;
public DomResultFactory(DocumentBuilderFactory documentBuilderFactory) {
this.documentBuilderFactory = documentBuilderFactory;
public DomResultFactory() {
this(DocumentBuilderFactoryUtils.newInstance());
this.documentBuilderFactory.setNamespaceAware(true);
}
public DomResultFactory() {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true);
this.documentBuilderFactory = factory;
public DomResultFactory(DocumentBuilderFactory documentBuilderFactory) {
Assert.notNull(documentBuilderFactory, "'documentBuilderFactory' must not be null.");
this.documentBuilderFactory = documentBuilderFactory;
}

View File

@@ -21,7 +21,6 @@ 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;
@@ -41,6 +40,8 @@ import org.springframework.xml.validation.XmlValidatorFactory;
* @author Oleg Zhurakousky
* @author Gary Russell
* @author Liujiong
* @author Artem Bilan
*
* @since 2.0
*/
public class XmlValidatingMessageSelector implements MessageSelector {
@@ -63,19 +64,13 @@ public class XmlValidatingMessageSelector implements MessageSelector {
}
private final Log logger = LogFactory.getLog(this.getClass());
private final Log logger = LogFactory.getLog(getClass());
private final XmlValidator xmlValidator;
private volatile boolean throwExceptionOnRejection;
private boolean throwExceptionOnRejection;
private volatile XmlPayloadConverter converter = new DefaultXmlPayloadConverter();
public XmlValidatingMessageSelector(XmlValidator xmlValidator) {
Assert.notNull(xmlValidator, "XmlValidator must not be null");
this.xmlValidator = xmlValidator;
}
private XmlPayloadConverter converter = new DefaultXmlPayloadConverter();
/**
@@ -83,23 +78,28 @@ public class XmlValidatingMessageSelector implements MessageSelector {
* 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());
this(XmlValidatorFactory.createValidator(schema,
schemaType == null
? SchemaType.XML_SCHEMA.getUrl()
: schemaType.getUrl()));
}
public XmlValidatingMessageSelector(XmlValidator xmlValidator) {
Assert.notNull(xmlValidator, "XmlValidator must not be null");
this.xmlValidator = xmlValidator;
}
public XmlValidatingMessageSelector(Resource schema, String schemaType) throws IOException {
this(schema, StringUtils.isEmpty(schemaType) ? null :
SchemaType.valueOf(schemaType.toUpperCase().replaceFirst("-", "_")));
this(schema,
StringUtils.isEmpty(schemaType)
? null
: SchemaType.valueOf(schemaType.toUpperCase().replaceFirst("-", "_")));
}
@@ -119,7 +119,7 @@ public class XmlValidatingMessageSelector implements MessageSelector {
@Override
public boolean accept(Message<?> message) {
SAXParseException[] validationExceptions = null;
Throwable[] validationExceptions = null;
try {
validationExceptions = this.xmlValidator.validate(this.converter.convertToSource(message.getPayload()));
}
@@ -130,10 +130,12 @@ public class XmlValidatingMessageSelector implements MessageSelector {
if (!validationSuccess) {
if (this.throwExceptionOnRejection) {
throw new MessageRejectedException(message, "Message was rejected due to XML Validation errors",
new AggregatedXmlMessageValidationException(
Arrays.<Throwable>asList(validationExceptions)));
new AggregatedXmlMessageValidationException(Arrays.asList(validationExceptions)));
}
else if (this.logger.isInfoEnabled()) {
this.logger.info("Message was rejected due to XML Validation errors",
new AggregatedXmlMessageValidationException(Arrays.asList(validationExceptions)));
}
this.logger.debug("Message was rejected due to XML Validation errors");
}
return validationSuccess;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2019 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.
@@ -29,6 +29,8 @@ import org.w3c.dom.Document;
import org.xml.sax.InputSource;
import org.springframework.messaging.MessagingException;
import org.springframework.util.Assert;
import org.springframework.xml.DocumentBuilderFactoryUtils;
/**
* {@link SourceFactory} implementation which supports creation of a {@link DOMSource}
@@ -36,6 +38,7 @@ import org.springframework.messaging.MessagingException;
*
* @author Jonas Partner
* @author Mark Fisher
* @author Artem Bilan
*/
public class DomSourceFactory implements SourceFactory {
@@ -43,12 +46,12 @@ public class DomSourceFactory implements SourceFactory {
public DomSourceFactory() {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true);
this.documentBuilderFactory = factory;
this(DocumentBuilderFactoryUtils.newInstance());
this.documentBuilderFactory.setNamespaceAware(true);
}
public DomSourceFactory(DocumentBuilderFactory documentBuilderFactory) {
Assert.notNull(documentBuilderFactory, "'documentBuilderFactory' must not be null.");
this.documentBuilderFactory = documentBuilderFactory;
}
@@ -87,7 +90,7 @@ public class DomSourceFactory implements SourceFactory {
private DOMSource createDomSourceForFile(File file) {
try {
Document document = this.getNewDocumentBuilder().parse(file);
Document document = getNewDocumentBuilder().parse(file);
return new DOMSource(document.getDocumentElement());
}
catch (Exception e) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2019 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.
@@ -27,9 +27,11 @@ import javax.xml.transform.dom.DOMSource;
import org.w3c.dom.Document;
import org.springframework.messaging.MessagingException;
import org.springframework.util.Assert;
import org.springframework.util.FileCopyUtils;
import org.springframework.xml.transform.StringResult;
import org.springframework.xml.transform.StringSource;
import org.springframework.xml.transform.TransformerFactoryUtils;
/**
* {@link SourceFactory} implementation which supports creation of a {@link StringSource}
@@ -37,6 +39,7 @@ import org.springframework.xml.transform.StringSource;
*
* @author Jonas Partner
* @author Mark Fisher
* @author Artem Bilan
*/
public class StringSourceFactory implements SourceFactory {
@@ -44,10 +47,11 @@ public class StringSourceFactory implements SourceFactory {
public StringSourceFactory() {
this(TransformerFactory.newInstance());
this(TransformerFactoryUtils.newInstance());
}
public StringSourceFactory(TransformerFactory transformerFactory) {
Assert.notNull(transformerFactory, "'transformerFactory' must not be null.");
this.transformerFactory = transformerFactory;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2019 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.
@@ -49,8 +49,10 @@ import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHandlingException;
import org.springframework.messaging.converter.MessageConversionException;
import org.springframework.util.Assert;
import org.springframework.xml.DocumentBuilderFactoryUtils;
import org.springframework.xml.namespace.SimpleNamespaceContext;
import org.springframework.xml.transform.StringResult;
import org.springframework.xml.transform.TransformerFactoryUtils;
import org.springframework.xml.xpath.XPathException;
import org.springframework.xml.xpath.XPathExpression;
import org.springframework.xml.xpath.XPathExpressionFactory;
@@ -69,7 +71,7 @@ import org.springframework.xml.xpath.XPathExpressionFactory;
*/
public class XPathMessageSplitter extends AbstractMessageSplitter {
private final TransformerFactory transformerFactory = TransformerFactory.newInstance();
private final TransformerFactory transformerFactory;
private final Object documentBuilderFactoryMonitor = new Object();
@@ -77,22 +79,49 @@ public class XPathMessageSplitter extends AbstractMessageSplitter {
private javax.xml.xpath.XPathExpression jaxpExpression;
private volatile boolean createDocuments;
private boolean createDocuments;
private volatile DocumentBuilderFactory documentBuilderFactory;
private DocumentBuilderFactory documentBuilderFactory;
private volatile XmlPayloadConverter xmlPayloadConverter = new DefaultXmlPayloadConverter();
private XmlPayloadConverter xmlPayloadConverter = new DefaultXmlPayloadConverter();
private volatile Properties outputProperties;
private Properties outputProperties;
private volatile boolean iterator = true;
private boolean iterator = true;
public XPathMessageSplitter(String expression) {
this(expression, new HashMap<String, String>());
}
/**
* Construct an instance based on the provided xpath expression and
* {@link TransformerFactory}.
* @param expression the xpath expression for splitting.
* @param transformerFactory the {@link TransformerFactory}
* for parsing and building documents.
* @since 4.3.19
*/
public XPathMessageSplitter(String expression, TransformerFactory transformerFactory) {
this(expression, new HashMap<String, String>(), transformerFactory);
}
public XPathMessageSplitter(String expression, Map<String, String> namespaces) {
this(XPathExpressionFactory.createXPathExpression(expression, namespaces));
this(expression, namespaces, TransformerFactoryUtils.newInstance());
}
/**
* Construct an instance based on the provided xpath expression, namespaces and
* {@link TransformerFactory}.
* @param expression the xpath expression for splitting.
* @param namespaces the XML namespace for parsing.
* @param transformerFactory the {@link TransformerFactory}
* for parsing and building documents.
* @since 4.3.19
*/
public XPathMessageSplitter(String expression, Map<String, String> namespaces,
TransformerFactory transformerFactory) {
this(XPathExpressionFactory.createXPathExpression(expression, namespaces), transformerFactory);
XPath xpath = XPathFactory.newInstance().newXPath();
SimpleNamespaceContext namespaceContext = new SimpleNamespaceContext();
@@ -108,12 +137,25 @@ public class XPathMessageSplitter extends AbstractMessageSplitter {
}
public XPathMessageSplitter(XPathExpression xpathExpression) {
Assert.notNull(xpathExpression, "'xpathExpression' must not be null.");
this.xpathExpression = xpathExpression;
this.documentBuilderFactory = DocumentBuilderFactory.newInstance();
this.documentBuilderFactory.setNamespaceAware(true);
this(xpathExpression, TransformerFactoryUtils.newInstance());
}
/**
* Construct an instance based on the provided xpath expression and
* {@link TransformerFactory}.
* @param xpathExpression the xpath expression for splitting.
* @param transformerFactory the {@link TransformerFactory}
* for parsing and building documents.
* @since 4.3.19
*/
public XPathMessageSplitter(XPathExpression xpathExpression, TransformerFactory transformerFactory) {
Assert.notNull(xpathExpression, "'xpathExpression' must not be null.");
Assert.notNull(transformerFactory, "'transformerFactory' must not be null.");
this.xpathExpression = xpathExpression;
this.transformerFactory = transformerFactory;
this.documentBuilderFactory = DocumentBuilderFactoryUtils.newInstance();
this.documentBuilderFactory.setNamespaceAware(true);
}
public void setCreateDocuments(boolean createDocuments) {
this.createDocuments = createDocuments;
@@ -277,7 +319,7 @@ public class XPathMessageSplitter extends AbstractMessageSplitter {
private int index;
private NodeListIterator(NodeList nodeList) throws ParserConfigurationException {
NodeListIterator(NodeList nodeList) throws ParserConfigurationException {
this.nodeList = nodeList;
if (XPathMessageSplitter.this.createDocuments) {
this.documentBuilder = getNewDocumentBuilder();

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2010 the original author or authors.
* Copyright 2002-2019 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.
@@ -28,6 +28,8 @@ import org.w3c.dom.Document;
import org.xml.sax.InputSource;
import org.springframework.messaging.MessagingException;
import org.springframework.util.Assert;
import org.springframework.xml.DocumentBuilderFactoryUtils;
import org.springframework.xml.transform.StringResult;
/**
@@ -35,6 +37,7 @@ import org.springframework.xml.transform.StringResult;
* {@link DOMResult} and {@link StringResult} implementations.
*
* @author Jonas Partner
* @author Artem Bilan
*/
public class ResultToDocumentTransformer implements ResultTransformer {
@@ -42,18 +45,19 @@ public class ResultToDocumentTransformer implements ResultTransformer {
private final DocumentBuilderFactory documentBuilderFactory;
public ResultToDocumentTransformer(DocumentBuilderFactory documentBuilderFactory) {
this.documentBuilderFactory = documentBuilderFactory;
public ResultToDocumentTransformer() {
this(DocumentBuilderFactoryUtils.newInstance());
this.documentBuilderFactory.setNamespaceAware(true);
}
public ResultToDocumentTransformer() {
this.documentBuilderFactory = DocumentBuilderFactory.newInstance();
this.documentBuilderFactory.setNamespaceAware(true);
public ResultToDocumentTransformer(DocumentBuilderFactory documentBuilderFactory) {
Assert.notNull(documentBuilderFactory, "'documentBuilderFactory' must not be null.");
this.documentBuilderFactory = documentBuilderFactory;
}
public Object transformResult(Result result) {
Document document = null;
Document document;
if (DOMResult.class.isAssignableFrom(result.getClass())) {
document = createDocumentFromDomResult((DOMResult) result);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2010 the original author or authors.
* Copyright 2002-2019 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.
@@ -27,7 +27,9 @@ import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.dom.DOMSource;
import org.springframework.messaging.MessagingException;
import org.springframework.util.Assert;
import org.springframework.xml.transform.StringResult;
import org.springframework.xml.transform.TransformerFactoryUtils;
/**
* Converts the passed {@link Result} to an instance of {@link String}.
@@ -35,16 +37,27 @@ import org.springframework.xml.transform.StringResult;
*
* @author Jonas Partner
* @author Mark Fisher
* @author Artem Bilan
*/
public class ResultToStringTransformer implements ResultTransformer {
private volatile Properties outputProperties;
private final TransformerFactory transformerFactory;
private Properties outputProperties;
public ResultToStringTransformer() {
this.transformerFactory = TransformerFactory.newInstance();
this.transformerFactory = TransformerFactoryUtils.newInstance();
}
/**
* Construct an instance based on the provided {@link TransformerFactory}.
* @param transformerFactory the {@link TransformerFactory} to use.
* @since 4.3.19
*/
public ResultToStringTransformer(TransformerFactory transformerFactory) {
Assert.notNull(transformerFactory, "'transformerFactory' must not be null.");
this.transformerFactory = transformerFactory;
}
@@ -55,12 +68,12 @@ public class ResultToStringTransformer implements ResultTransformer {
public Object transformResult(Result result) {
String returnString = null;
if (result instanceof StringResult) {
returnString = ((StringResult) result).toString();
returnString = result.toString();
}
else if (result instanceof DOMResult) {
try {
StringResult stringResult = new StringResult();
this.getNewTransformer().transform(
getNewTransformer().transform(
new DOMSource(((DOMResult) result).getNode()), stringResult);
returnString = stringResult.toString();
}
@@ -76,7 +89,7 @@ public class ResultToStringTransformer implements ResultTransformer {
}
private Transformer getNewTransformer() throws TransformerConfigurationException {
Transformer transformer = null;
Transformer transformer;
synchronized (this.transformerFactory) {
transformer = this.transformerFactory.newTransformer();
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2019 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.
@@ -21,10 +21,12 @@ import java.util.Arrays;
import java.util.Map;
import java.util.Map.Entry;
import javax.xml.XMLConstants;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Templates;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMResult;
@@ -34,7 +36,10 @@ import javax.xml.transform.stream.StreamSource;
import org.w3c.dom.Document;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.VfsResource;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.integration.expression.ExpressionUtils;
@@ -45,11 +50,13 @@ import org.springframework.integration.xml.source.SourceFactory;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessagingException;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.PatternMatchUtils;
import org.springframework.util.StringUtils;
import org.springframework.xml.transform.StringResult;
import org.springframework.xml.transform.StringSource;
import org.springframework.xml.transform.TransformerFactoryUtils;
/**
* Thread safe XSLT transformer implementation which returns a transformed
@@ -80,11 +87,24 @@ import org.springframework.xml.transform.StringSource;
*/
public class XsltPayloadTransformer extends AbstractXmlTransformer implements BeanClassLoaderAware {
private static final Class<?> SERVLET_CONTEXT_RESOURCE_CLASS;
static {
Class<?> theClass = null;
try {
theClass = ClassUtils.forName("org.springframework.web.context.support.ServletContextResource", null);
}
catch (ClassNotFoundException e) {
//
}
SERVLET_CONTEXT_RESOURCE_CLASS = theClass;
}
private final ResultTransformer resultTransformer;
private volatile Resource xslResource;
private final Resource xslResource;
private volatile Templates templates;
private Templates templates;
private String transformerFactoryClassName;
@@ -92,15 +112,15 @@ public class XsltPayloadTransformer extends AbstractXmlTransformer implements Be
private Map<String, Expression> xslParameterMappings;
private volatile SourceFactory sourceFactory = new DomSourceFactory();
private SourceFactory sourceFactory = new DomSourceFactory();
private volatile boolean resultFactoryExplicitlySet;
private boolean resultFactoryExplicitlySet;
private volatile boolean alwaysUseSourceFactory = false;
private boolean alwaysUseSourceFactory = false;
private volatile boolean alwaysUseResultFactory = false;
private boolean alwaysUseResultFactory = false;
private volatile String[] xsltParamHeaders;
private String[] xsltParamHeaders;
private ClassLoader classLoader;
@@ -109,6 +129,13 @@ public class XsltPayloadTransformer extends AbstractXmlTransformer implements Be
this(templates, null);
}
public XsltPayloadTransformer(Templates templates, ResultTransformer resultTransformer) {
Assert.notNull(templates, "'templates' must not be null.");
this.templates = templates;
this.resultTransformer = resultTransformer;
this.xslResource = null;
}
public XsltPayloadTransformer(Resource xslResource) {
this(xslResource, null, null);
}
@@ -118,29 +145,27 @@ public class XsltPayloadTransformer extends AbstractXmlTransformer implements Be
}
public XsltPayloadTransformer(Resource xslResource, String transformerFactoryClassName) {
Assert.notNull(xslResource, "'xslResource' must not be null.");
Assert.hasText(transformerFactoryClassName, "'transformerFactoryClassName' must not be empty String.");
this.xslResource = xslResource;
this.transformerFactoryClassName = transformerFactoryClassName;
this.resultTransformer = null;
this(xslResource, null, transformerFactoryClassName);
}
public XsltPayloadTransformer(Resource xslResource, ResultTransformer resultTransformer, String transformerFactoryClassName) {
public XsltPayloadTransformer(Resource xslResource, ResultTransformer resultTransformer,
String transformerFactoryClassName) {
Assert.notNull(xslResource, "'xslResource' must not be null.");
Assert.isTrue(xslResource instanceof ClassPathResource || xslResource instanceof FileSystemResource ||
xslResource instanceof VfsResource ||
(SERVLET_CONTEXT_RESOURCE_CLASS != null
&& SERVLET_CONTEXT_RESOURCE_CLASS.isAssignableFrom(xslResource.getClass())),
"Only 'ClassPathResource', 'FileSystemResource', 'ServletContextResource' or 'VfsResource'" +
" are supported directly in this transformer. For any other 'Resource' implementations" +
" consider to use a 'Templates'-based constructor instantiation.");
this.xslResource = xslResource;
this.resultTransformer = resultTransformer;
this.transformerFactoryClassName = transformerFactoryClassName;
}
public XsltPayloadTransformer(Templates templates, ResultTransformer resultTransformer) {
Assert.notNull(templates, "'templates' must not be null.");
this.templates = templates;
this.resultTransformer = resultTransformer;
}
/**
* Sets the SourceFactory.
*
* @param sourceFactory The source factory.
*/
public void setSourceFactory(SourceFactory sourceFactory) {
@@ -150,7 +175,6 @@ public class XsltPayloadTransformer extends AbstractXmlTransformer implements Be
/**
* Sets the ResultFactory.
*
* @param resultFactory The result factory.
*/
@Override
@@ -161,7 +185,6 @@ public class XsltPayloadTransformer extends AbstractXmlTransformer implements Be
/**
* Specify whether to always use source factory even for directly supported payload types.
*
* @param alwaysUseSourceFactory true to always use the source factory.
*/
public void setAlwaysUseSourceFactory(boolean alwaysUseSourceFactory) {
@@ -170,7 +193,6 @@ public class XsltPayloadTransformer extends AbstractXmlTransformer implements Be
/**
* Specify whether to always use result factory even for directly supported payload types
*
* @param alwaysUseResultFactory true to always use the result factory.
*/
public void setAlwaysUseResultFactory(boolean alwaysUseResultFactory) {
@@ -181,7 +203,8 @@ public class XsltPayloadTransformer extends AbstractXmlTransformer implements Be
this.xslParameterMappings = xslParameterMappings;
}
public void setXsltParamHeaders(String[] xsltParamHeaders) {
public void setXsltParamHeaders(String... xsltParamHeaders) {
Assert.notNull(xsltParamHeaders, "'xsltParamHeaders' must not be null.");
this.xsltParamHeaders = Arrays.copyOf(xsltParamHeaders, xsltParamHeaders.length);
}
@@ -215,16 +238,27 @@ public class XsltPayloadTransformer extends AbstractXmlTransformer implements Be
@Override
protected void onInit() throws Exception {
super.onInit();
this.evaluationContext = ExpressionUtils.createStandardEvaluationContext(this.getBeanFactory());
this.evaluationContext = ExpressionUtils.createStandardEvaluationContext(getBeanFactory());
if (this.templates == null) {
TransformerFactory transformerFactory;
if (this.transformerFactoryClassName != null) {
transformerFactory = TransformerFactory.newInstance(this.transformerFactoryClassName, this.classLoader);
transformerFactory = TransformerFactory.newInstance(this.transformerFactoryClassName,
this.classLoader);
}
else {
transformerFactory = TransformerFactory.newInstance();
transformerFactory = TransformerFactoryUtils.newInstance();
}
transformerFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
transformerFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, "file,jar:file");
try {
this.templates = transformerFactory.newTemplates(createStreamSourceOnResource(this.xslResource));
}
catch (TransformerConfigurationException e) {
throw new IllegalStateException(e);
}
catch (IOException e) {
throw new IllegalStateException(e);
}
this.templates = transformerFactory.newTemplates(createStreamSourceOnResource(this.xslResource));
}
}
@@ -278,13 +312,16 @@ public class XsltPayloadTransformer extends AbstractXmlTransformer implements Be
return transformSource(source, payload, transformer);
}
private Object transformSource(Source source, Object payload, Transformer transformer) throws TransformerException {
private Object transformSource(Source source, Object payload, Transformer transformer)
throws TransformerException {
Result result;
if (!this.resultFactoryExplicitlySet && "text".equals(transformer.getOutputProperties().getProperty("method"))) {
if (!this.resultFactoryExplicitlySet &&
"text".equals(transformer.getOutputProperties().getProperty("method"))) {
result = new StringResult();
}
else {
result = this.getResultFactory().createResult(payload);
result = getResultFactory().createResult(payload);
}
transformer.transform(source, result);
if (this.resultTransformer != null) {
@@ -314,7 +351,7 @@ public class XsltPayloadTransformer extends AbstractXmlTransformer implements Be
else {
source = new DOMSource(documentPayload);
}
Result result = this.getResultFactory().createResult(documentPayload);
Result result = getResultFactory().createResult(documentPayload);
if (!DOMResult.class.isAssignableFrom(result.getClass())) {
throw new MessagingException(
"Document to Document conversion requires a DOMResult-producing ResultFactory implementation.");

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014 the original author or authors.
* Copyright 2014-2019 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.
@@ -38,31 +38,55 @@ import org.springframework.xml.xpath.XPathExpressionFactory;
* @author Jonas Partner
* @author Mark Fisher
* @author Artem Bilan
*
* @since 2.0
*/
public class XPathExpressionEvaluatingHeaderValueMessageProcessor implements HeaderValueMessageProcessor<Object>,
BeanFactoryAware {
private static final XmlPayloadConverter converter = new DefaultXmlPayloadConverter();
private final BeanFactoryTypeConverter typeConverter = new BeanFactoryTypeConverter();
private final XPathExpression expression;
private volatile XPathEvaluationType evaluationType = XPathEvaluationType.STRING_RESULT;
private final XmlPayloadConverter converter;
private volatile TypeDescriptor headerTypeDescriptor;
private XPathEvaluationType evaluationType = XPathEvaluationType.STRING_RESULT;
private volatile Boolean overwrite = null;
private TypeDescriptor headerTypeDescriptor;
private Boolean overwrite;
public XPathExpressionEvaluatingHeaderValueMessageProcessor(String expression) {
Assert.hasText(expression, "expression must have text");
this.expression = XPathExpressionFactory.createXPathExpression(expression);
this(expression, new DefaultXmlPayloadConverter());
}
/**
* Construct an instance based on the provided xpath expression and {@link XmlPayloadConverter}.
* @param expression the xpath expression to evaluate.
* @param converter the {@link XmlPayloadConverter} to use for document conversion.
* @since 4.3.19
*/
public XPathExpressionEvaluatingHeaderValueMessageProcessor(String expression, XmlPayloadConverter converter) {
this(XPathExpressionFactory.createXPathExpression(expression), converter);
}
public XPathExpressionEvaluatingHeaderValueMessageProcessor(XPathExpression expression) {
Assert.notNull(expression, "expression must not be null");
this(expression, new DefaultXmlPayloadConverter());
}
/**
* Construct an instance based on the provided xpath expression and {@link XmlPayloadConverter}.
* @param expression the xpath expression to evaluate.
* @param converter the {@link XmlPayloadConverter} to use for document conversion.
* @since 4.3.19
*/
public XPathExpressionEvaluatingHeaderValueMessageProcessor(XPathExpression expression,
XmlPayloadConverter converter) {
Assert.notNull(expression, "'expression' must not be null.");
Assert.notNull(converter, "'converter' must not be null.");
this.expression = expression;
this.converter = converter;
}
public void setEvaluationType(XPathEvaluationType evaluationType) {
@@ -94,7 +118,7 @@ public class XPathExpressionEvaluatingHeaderValueMessageProcessor implements Hea
@Override
public Object processMessage(Message<?> message) {
Node node = converter.convertToNode(message.getPayload());
Node node = this.converter.convertToNode(message.getPayload());
Object result = this.evaluationType.evaluateXPath(this.expression, node);
if (result instanceof String && ((String) result).length() == 0) {
result = null;
@@ -106,4 +130,5 @@ public class XPathExpressionEvaluatingHeaderValueMessageProcessor implements Hea
return result;
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2014 the original author or authors.
* Copyright 2013-2019 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.
@@ -30,8 +30,8 @@ import org.w3c.dom.Node;
import org.springframework.integration.xml.DefaultXmlPayloadConverter;
import org.springframework.integration.xml.XmlPayloadConverter;
import org.springframework.messaging.MessagingException;
import org.springframework.util.Assert;
import org.springframework.xml.DocumentBuilderFactoryUtils;
import org.springframework.xml.xpath.NodeMapper;
import org.springframework.xml.xpath.XPathException;
import org.springframework.xml.xpath.XPathExpression;
@@ -41,6 +41,7 @@ import org.springframework.xml.xpath.XPathExpressionFactory;
* Utility class for 'xpath' support.
*
* @author Artem Bilan
*
* @since 3.0
*/
public final class XPathUtils {
@@ -57,31 +58,36 @@ public final class XPathUtils {
public static final String DOCUMENT_LIST = "document_list";
private static List<String> RESULT_TYPES = Arrays.asList(STRING, BOOLEAN, NUMBER, NODE, NODE_LIST, DOCUMENT_LIST);
private static final List<String> RESULT_TYPES =
Arrays.asList(STRING, BOOLEAN, NUMBER, NODE, NODE_LIST, DOCUMENT_LIST);
private static XmlPayloadConverter converter = new DefaultXmlPayloadConverter();
private static final XmlPayloadConverter converter = new DefaultXmlPayloadConverter();
private static DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
private static final DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactoryUtils.newInstance();
static {
documentBuilderFactory.setNamespaceAware(true);
}
/**
* Utility method to evaluate an xpath on the provided object.
* Delegates evaluation to an {@link XPathExpression}.
* Note this method provides the {@code #xpath()} SpEL function.
*
*
* @param o the xml Object for evaluaton.
* @param xpath an 'xpath' expression String.
* @param object the xml Object for evaluation.
* @param xpath an 'xpath' expression String.
* @param resultArg an optional parameter to represent the result type of the xpath evaluation.
* Only one argument is allowed, which can be an instance of {@link org.springframework.xml.xpath.NodeMapper} or
* one of these String constants: "string", "boolean", "number", "node" or "node_list".
* @param <T> The required return type.
* Only one argument is allowed, which can be an instance of
* {@link org.springframework.xml.xpath.NodeMapper} or
* one of these String constants: "string", "boolean", "number", "node" or "node_list".
* @param <T> The required return type.
* @return the result of the xpath expression evaluation.
* @throws IllegalArgumentException - if the provided arguments aren't appropriate types or values;
* @throws MessagingException - if the provided object can't be converted to a {@link Node};
* @throws org.springframework.messaging.MessagingException - if the provided
* object can't be converted to a {@link Node};
* @throws XPathException - if the xpath expression can't be evaluated.
*/
@SuppressWarnings({"unchecked"})
public static <T> T evaluate(Object o, String xpath, Object... resultArg) {
@SuppressWarnings({ "unchecked" })
public static <T> T evaluate(Object object, String xpath, Object... resultArg) {
Object resultType = null;
if (resultArg != null && resultArg.length > 0) {
Assert.isTrue(resultArg.length == 1, "'resultArg' can contains only one element.");
@@ -90,7 +96,7 @@ public final class XPathUtils {
}
XPathExpression expression = XPathExpressionFactory.createXPathExpression(xpath);
Node node = converter.convertToNode(o);
Node node = converter.convertToNode(object);
if (resultType == null) {
return (T) expression.evaluateAsString(node);
@@ -101,7 +107,8 @@ public final class XPathUtils {
else if (resultType instanceof String && RESULT_TYPES.contains(resultType)) {
String resType = (String) resultType;
if (DOCUMENT_LIST.equals(resType)) {
List<Node> nodeList = (List<Node>) XPathEvaluationType.NODE_LIST_RESULT.evaluateXPath(expression, node);
List<Node> nodeList = (List<Node>) XPathEvaluationType.NODE_LIST_RESULT.evaluateXPath(expression,
node);
try {
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
List<Node> documents = new ArrayList<Node>(nodeList.size());

View File

@@ -367,6 +367,22 @@
<xsd:union memberTypes="xsd:boolean xsd:string" />
</xsd:simpleType>
</xsd:attribute>
<xsd:attribute name="converter">
<xsd:annotation>
<xsd:documentation>
Specify the XmlPayloadConverter to use when converting a Message payload prior
to XPath evaluation.
The DefaultXmlPayloadConverter is used if this reference is not provided, and it
should be sufficient in most cases since it can convert from Node, Document, Source,
File, and String typed payloads.
</xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.integration.xml.XmlPayloadConverter"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>

View File

@@ -53,8 +53,14 @@
<header name="foo" xpath-expression="/person/@name" overwrite="true" />
</xpath-header-enricher>
<xpath-header-enricher id="customHeaderEnricher" input-channel="customInput" default-overwrite="true" should-skip-nulls="false">
<xpath-header-enricher id="customHeaderEnricher"
input-channel="customInput"
default-overwrite="true"
should-skip-nulls="false"
converter="xmlPayloadConverter">
<header name="foo" xpath-expression="/person/@name" overwrite="false" />
</xpath-header-enricher>
<beans:bean id="xmlPayloadConverter" class="org.springframework.integration.xml.DefaultXmlPayloadConverter"/>
</beans:beans>

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2019 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.
@@ -17,12 +17,16 @@
package org.springframework.integration.xml.config;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.instanceOf;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import java.util.List;
import java.util.Map;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -37,12 +41,13 @@ import org.springframework.integration.endpoint.EventDrivenConsumer;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.integration.support.SmartLifecycleRoleController;
import org.springframework.integration.test.util.TestUtils;
import org.springframework.integration.transformer.support.HeaderValueMessageProcessor;
import org.springframework.integration.xml.transformer.support.XPathExpressionEvaluatingHeaderValueMessageProcessor;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.PollableChannel;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.util.MultiValueMap;
/**
@@ -52,8 +57,7 @@ import org.springframework.util.MultiValueMap;
*
* @since 2.0
*/
@ContextConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
@RunWith(SpringRunner.class)
@DirtiesContext
public class XPathHeaderEnricherParserTests {
@@ -66,11 +70,12 @@ public class XPathHeaderEnricherParserTests {
@Autowired
private ApplicationContext context;
private final Message<?> message = MessageBuilder.withPayload("<person name='John Doe' age='42' married='true'/>").build();
private final Message<?> message =
MessageBuilder.withPayload("<person name='John Doe' age='42' married='true'/>").build();
@Test
public void testParse() throws Exception {
public void testParse() {
EventDrivenConsumer consumer = (EventDrivenConsumer) context.getBean("parseOnly");
assertEquals(2, TestUtils.getPropertyValue(consumer, "handler.order"));
assertEquals(123L, TestUtils.getPropertyValue(consumer, "handler.messagingTemplate.sendTimeout"));
@@ -115,35 +120,36 @@ public class XPathHeaderEnricherParserTests {
public void nodeListResult() {
Message<?> result = this.getResultMessage();
Object header = result.getHeaders().get("node-list-test");
assertTrue(List.class.isAssignableFrom(header.getClass()));
assertThat(header, instanceOf(List.class));
List<Node> nodeList = (List<Node>) header;
assertNotNull(nodeList);
assertEquals(3, nodeList.size());
}
@Test
public void expressionRef() {
Message<?> result = this.getResultMessage();
assertEquals(new Double(84), result.getHeaders().get("ref-test"));
Message<?> result = getResultMessage();
assertEquals(84d, result.getHeaders().get("ref-test"));
}
@Test
public void defaultOverwrite() {
assertEquals(false, this.getEnricherProperty("defaultHeaderEnricher", "defaultOverwrite"));
public void testDefaultHeaderEnricher() {
assertFalse(getEnricherProperty("defaultHeaderEnricher", "defaultOverwrite"));
assertTrue(getEnricherProperty("defaultHeaderEnricher", "shouldSkipNulls"));
}
@Test
public void defaultShouldSkipNulls() {
assertEquals(true, this.getEnricherProperty("defaultHeaderEnricher", "shouldSkipNulls"));
}
@Test
public void customOverwrite() {
assertEquals(true, this.getEnricherProperty("customHeaderEnricher", "defaultOverwrite"));
}
@Test
public void customShouldSkipNulls() {
assertEquals(false, this.getEnricherProperty("customHeaderEnricher", "shouldSkipNulls"));
@SuppressWarnings("unchecked")
public void testCustomHeaderEnricher() {
assertTrue(getEnricherProperty("customHeaderEnricher", "defaultOverwrite"));
assertFalse(getEnricherProperty("customHeaderEnricher", "shouldSkipNulls"));
Map<String, ? extends HeaderValueMessageProcessor<?>> headersToAdd =
TestUtils.getPropertyValue(this.context.getBean("customHeaderEnricher"),
"handler.transformer.headersToAdd", Map.class);
HeaderValueMessageProcessor<?> headerValueMessageProcessor = headersToAdd.get("foo");
assertThat(headerValueMessageProcessor, instanceOf(XPathExpressionEvaluatingHeaderValueMessageProcessor.class));
assertSame(this.context.getBean("xmlPayloadConverter"),
TestUtils.getPropertyValue(headerValueMessageProcessor, "converter"));
}
@Test
@@ -155,6 +161,7 @@ public class XPathHeaderEnricherParserTests {
.build();
this.context.getBean("defaultInput", MessageChannel.class).send(request);
Message<?> reply = replyChannel.receive();
assertNotNull(reply);
assertEquals("John Doe", reply.getHeaders().get("foo"));
}
@@ -167,6 +174,7 @@ public class XPathHeaderEnricherParserTests {
.build();
this.context.getBean("customInput", MessageChannel.class).send(request);
Message<?> reply = replyChannel.receive();
assertNotNull(reply);
assertEquals("bar", reply.getHeaders().get("foo"));
}
@@ -180,7 +188,7 @@ public class XPathHeaderEnricherParserTests {
Object endpoint = this.context.getBean(beanName);
Object handler = new DirectFieldAccessor(endpoint).getPropertyValue("handler");
Object enricher = new DirectFieldAccessor(handler).getPropertyValue("transformer");
return ((Boolean) new DirectFieldAccessor(enricher).getPropertyValue(propertyName)).booleanValue();
return (boolean) new DirectFieldAccessor(enricher).getPropertyValue(propertyName);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2019 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.
@@ -20,25 +20,32 @@ import static org.custommonkey.xmlunit.XMLAssert.assertXMLEqual;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.io.File;
import java.io.IOException;
import javax.xml.transform.Result;
import javax.xml.transform.Templates;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.TransformerFactoryConfigurationError;
import javax.xml.transform.dom.DOMResult;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.mockito.Mockito;
import org.w3c.dom.Document;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessagingException;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.integration.xml.result.StringResultFactory;
import org.springframework.integration.xml.util.XmlTestUtil;
import org.springframework.messaging.MessagingException;
import org.springframework.messaging.support.GenericMessage;
import org.springframework.util.FileCopyUtils;
import org.springframework.xml.transform.StringResult;
import org.springframework.xml.transform.StringSource;
@@ -53,21 +60,26 @@ public class XsltPayloadTransformerTests {
private XsltPayloadTransformer transformer;
private final String docAsString = "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?><order><orderItem>test</orderItem></order>";
private final String docAsString = "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?><order><orderItem>test" +
"</orderItem></order>";
private final String outputAsString = "<bob>test</bob>";
@Rule
public TemporaryFolder temporaryFolder = new TemporaryFolder();
@Before
public void setUp() throws Exception {
transformer = new XsltPayloadTransformer(getXslResource());
transformer.setBeanFactory(Mockito.mock(BeanFactory.class));
transformer.afterPropertiesSet();
this.transformer = new XsltPayloadTransformer(getXslTemplates());
this.transformer.setBeanFactory(Mockito.mock(BeanFactory.class));
this.transformer.setAlwaysUseResultFactory(false);
this.transformer.afterPropertiesSet();
}
@Test
public void testDocumentAsPayload() throws Exception {
Object transformed = transformer.doTransform(buildMessage(XmlTestUtil
.getDocumentForString(docAsString)));
Object transformed =
transformer.doTransform(new GenericMessage<>(XmlTestUtil.getDocumentForString(docAsString)));
assertTrue("Wrong return type for document payload", Document.class
.isAssignableFrom(transformed.getClass()));
Document transformedDocument = (Document) transformed;
@@ -78,7 +90,7 @@ public class XsltPayloadTransformerTests {
@Test
public void testSourceAsPayload() throws Exception {
Object transformed = transformer
.doTransform(buildMessage(new StringSource(docAsString)));
.doTransform(new GenericMessage<>(new StringSource(docAsString)));
assertEquals("Wrong return type for source payload", DOMResult.class,
transformed.getClass());
DOMResult result = (DOMResult) transformed;
@@ -89,7 +101,7 @@ public class XsltPayloadTransformerTests {
@Test
public void testStringAsPayload() throws Exception {
Object transformed = transformer.doTransform(buildMessage(docAsString));
Object transformed = transformer.doTransform(new GenericMessage<>(docAsString));
assertEquals("Wrong return type for string payload", String.class,
transformed.getClass());
String transformedString = (String) transformed;
@@ -100,7 +112,7 @@ public class XsltPayloadTransformerTests {
@Test
public void testStringAsPayloadUseResultFactoryTrue() throws Exception {
transformer.setAlwaysUseResultFactory(true);
Object transformed = transformer.doTransform(buildMessage(docAsString));
Object transformed = transformer.doTransform(new GenericMessage<>(docAsString));
assertEquals("Wrong return type for useFactories true",
DOMResult.class, transformed.getClass());
DOMResult result = (DOMResult) transformed;
@@ -111,46 +123,46 @@ public class XsltPayloadTransformerTests {
@Test
public void testSourceWithResultTransformer() throws Exception {
Integer returnValue = new Integer(13);
transformer = new XsltPayloadTransformer(getXslResource(),
new StubResultTransformer(returnValue));
Integer returnValue = 13;
XsltPayloadTransformer transformer =
new XsltPayloadTransformer(getXslTemplates(), new StubResultTransformer(returnValue));
transformer.setBeanFactory(Mockito.mock(BeanFactory.class));
transformer.afterPropertiesSet();
Object transformed = transformer
.doTransform(buildMessage(new StringSource(docAsString)));
.doTransform(new GenericMessage<>(new StringSource(docAsString)));
assertEquals("Wrong value from result conversion", returnValue,
transformed);
}
@Test
public void testXsltPayloadWithTransformerFactoryClassname() throws Exception {
Integer returnValue = new Integer(13);
transformer = new XsltPayloadTransformer(getXslResource(), new StubResultTransformer(returnValue),
"com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl");
public void testXsltPayloadWithTransformerFactoryClassName() throws Exception {
Integer returnValue = 13;
XsltPayloadTransformer transformer =
new XsltPayloadTransformer(getXslResourceThatOutputsText(), new StubResultTransformer(returnValue),
"com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl");
transformer.setBeanFactory(Mockito.mock(BeanFactory.class));
transformer.afterPropertiesSet();
Object transformed = transformer
.doTransform(buildMessage(new StringSource(docAsString)));
.doTransform(new GenericMessage<>(new StringSource(docAsString)));
assertEquals("Wrong value from result conversion", returnValue,
transformed);
}
@Test(expected = TransformerFactoryConfigurationError.class)
public void testXsltPayloadWithBadTransformerFactoryClassname() throws Exception {
transformer = new XsltPayloadTransformer(getXslResource(), "foo.bar.Baz");
transformer = new XsltPayloadTransformer(getXslResourceThatOutputsText(), "foo.bar.Baz");
transformer.setBeanFactory(Mockito.mock(BeanFactory.class));
transformer.afterPropertiesSet();
transformer.doTransform(buildMessage(new StringSource(docAsString)));
}
@Test(expected = TransformerException.class)
public void testNonXmlString() throws Exception {
transformer.doTransform(buildMessage("test"));
transformer.doTransform(new GenericMessage<>("test"));
}
@Test(expected = MessagingException.class)
public void testUnsupportedPayloadType() throws Exception {
transformer.doTransform(buildMessage(new Long(12)));
transformer.doTransform(new GenericMessage<>(12L));
}
@Test
@@ -160,7 +172,7 @@ public class XsltPayloadTransformerTests {
transformer = new XsltPayloadTransformer(resource);
transformer.setBeanFactory(Mockito.mock(BeanFactory.class));
transformer.afterPropertiesSet();
assertEquals(transformer.doTransform(buildMessage(docAsString)),
assertEquals(transformer.doTransform(new GenericMessage<>(docAsString)),
outputAsString);
}
@@ -174,7 +186,7 @@ public class XsltPayloadTransformerTests {
transformer.setAlwaysUseResultFactory(true);
transformer.setBeanFactory(Mockito.mock(BeanFactory.class));
transformer.afterPropertiesSet();
Object returned = transformer.doTransform(buildMessage(XmlTestUtil.getDocumentForString(docAsString)));
Object returned = transformer.doTransform(new GenericMessage<>(XmlTestUtil.getDocumentForString(docAsString)));
assertEquals("Wrong type of return ", StringResult.class, returned.getClass());
}
@@ -188,7 +200,7 @@ public class XsltPayloadTransformerTests {
transformer.setAlwaysUseResultFactory(true);
transformer.setBeanFactory(Mockito.mock(BeanFactory.class));
transformer.afterPropertiesSet();
Object returned = transformer.doTransform(buildMessage(XmlTestUtil.getDocumentForString(docAsString)));
Object returned = transformer.doTransform(new GenericMessage<>(XmlTestUtil.getDocumentForString(docAsString)));
assertEquals("Wrong type of return ", StringResult.class, returned.getClass());
}
@@ -199,32 +211,34 @@ public class XsltPayloadTransformerTests {
transformer.setAlwaysUseResultFactory(true);
transformer.setBeanFactory(Mockito.mock(BeanFactory.class));
transformer.afterPropertiesSet();
Object returned = transformer.doTransform(buildMessage(XmlTestUtil.getDocumentForString(docAsString)));
Object returned = transformer.doTransform(new GenericMessage<>(XmlTestUtil.getDocumentForString(docAsString)));
assertEquals("Wrong type of return ", StringResult.class, returned.getClass());
assertEquals("Wrong content in string", "hello world", returned.toString());
}
protected Message<?> buildMessage(Object payload) {
return MessageBuilder.withPayload(payload).build();
}
private Templates getXslTemplates() throws Exception {
TransformerFactory transformerFactory = TransformerFactory.newInstance();
private Resource getXslResource() throws Exception {
String xsl = "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>" +
"<xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">" +
" <xsl:template match=\"order\">" +
" <bob>test</bob>" +
" </xsl:template>" +
"</xsl:stylesheet>";
return new ByteArrayResource(xsl.getBytes("UTF-8"));
return transformerFactory.newTemplates(new StringSource(xsl));
}
private Resource getXslResourceThatOutputsText() throws Exception {
private Resource getXslResourceThatOutputsText() throws IOException {
String xsl = "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>" +
"<xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">" +
" <xsl:output method=\"text\" encoding=\"UTF-8\" />" +
" <xsl:template match=\"order\">hello world</xsl:template>" +
"</xsl:stylesheet>";
return new ByteArrayResource(xsl.getBytes("UTF-8"));
File xsltFile = this.temporaryFolder.newFile();
FileCopyUtils.copy(xsl.getBytes(), xsltFile);
return new FileSystemResource(xsltFile);
}
public static class StubResultTransformer implements ResultTransformer {
@@ -238,6 +252,7 @@ public class XsltPayloadTransformerTests {
public Object transformResult(Result result) {
return objectToReturn;
}
}
}