support for setting parameters and output properties on transformer instances used by the XsltPayloadTransformer

resolves INT-897
This commit is contained in:
Jonas Partner
2010-03-11 14:16:50 +00:00
parent 2dffbf32d0
commit 4a4a867f63
6 changed files with 386 additions and 51 deletions

View File

@@ -0,0 +1,85 @@
/*
* Copyright 2002-2008 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.transformer;
import java.util.HashMap;
import java.util.Map;
import javax.xml.transform.Transformer;
import org.springframework.integration.core.Message;
import org.springframework.integration.core.MessageHeaders;
import sun.tools.tree.OrExpression;
/**
* {@link TransformerConfigurer} instance which looks for headers and uses them
* to configure the provided {@link Transformer} instance. For example a header
* names xslt_paramter_X will cause the transfomer to be configured with a
* property names X with the value of the header. A property named
* xslt_output_property_X will cause an output property on the transformer to be
* set with this headers value.
*
*
* @author Jonas Partner
*
*/
public class DefaultTransformerConfigurer implements TransformerConfigurer {
public void configureTransfomer(Message<?> message, Transformer transformer) {
Map<String,Object> parameters = extractParameterHeaders(message.getHeaders());
for(String paramName: parameters.keySet()){
transformer.setParameter(paramName, parameters.get(paramName));
}
Map<String, String> outputProperties = extractOutputPropertyHeaders(message.getHeaders());
for(String outputPropertyName : outputProperties.keySet()){
transformer.setOutputProperty(outputPropertyName, outputProperties.get(outputPropertyName));
}
}
protected Map<String, String> extractOutputPropertyHeaders(
MessageHeaders headers) {
Map<String, String> parameters = new HashMap<String, String>();
int prefixStringLength = XsltHeaders.OUTPUT_PROPERTY.length();
for (String key : headers.keySet()) {
if (key.startsWith(XsltHeaders.OUTPUT_PROPERTY)) {
Object headerValue = headers.get(key);
if (!(headerValue instanceof String)) {
throw new IllegalArgumentException(
"Xslt Transfomer only support String output properties received header of type"
+ headerValue.getClass().getName()
+ " for header named " + key);
}
parameters.put(key.substring(prefixStringLength), (String)headerValue);
}
}
return parameters;
}
protected Map<String, Object> extractParameterHeaders(MessageHeaders headers) {
Map<String, Object> parameters = new HashMap<String, Object>();
int prefixStringLength = XsltHeaders.PARAMATER.length();
for (String key : headers.keySet()) {
if (key.startsWith(XsltHeaders.PARAMATER)) {
parameters.put(key.substring(prefixStringLength), headers.get(key));
}
}
return parameters;
}
}

View File

@@ -0,0 +1,39 @@
/*
* Copyright 2002-2008 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.transformer;
import javax.xml.transform.Transformer;
import org.springframework.integration.core.Message;
/**
* Allows customistation of the transformer based on the recevied message prior
* to transformation
*
* @author Jonas Partner
*
*/
public interface TransformerConfigurer {
/**
* Callback method called by Xslt transfomer implementations after transformer is creates
* @param message
* @param transformer
*/
public void configureTransfomer(Message<?> message, Transformer transformer);
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright 2002-2008 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.transformer;
import javax.xml.transform.Transformer;
/**
* Message headers that can be used to configure the {@link Transformer}
* instance used for Xsl transformation
*
* @author Jonas Partner
*
*/
public abstract class XsltHeaders {
public static final String PREFIX = "xslt_";
public static final String OUTPUT_PROPERTY = PREFIX + "output_property_";
public static final String PARAMATER = PREFIX + "parameter_";
}

View File

@@ -20,6 +20,7 @@ import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Templates;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMResult;
@@ -29,8 +30,10 @@ import javax.xml.transform.stream.StreamSource;
import org.w3c.dom.Document;
import org.springframework.core.io.Resource;
import org.springframework.integration.core.Message;
import org.springframework.integration.core.MessagingException;
import org.springframework.integration.transformer.AbstractPayloadTransformer;
import org.springframework.integration.transformer.AbstractTransformer;
import org.springframework.integration.xml.result.DomResultFactory;
import org.springframework.integration.xml.result.ResultFactory;
import org.springframework.integration.xml.source.DomSourceFactory;
@@ -64,7 +67,7 @@ import java.io.IOException;
* @author Jonas Partner
* @author Mark Fisher
*/
public class XsltPayloadTransformer extends AbstractPayloadTransformer<Object, Object> {
public class XsltPayloadTransformer extends AbstractTransformer {
private final Templates templates;
@@ -75,6 +78,8 @@ public class XsltPayloadTransformer extends AbstractPayloadTransformer<Object, O
private volatile ResultFactory resultFactory = new DomResultFactory();
private volatile boolean alwaysUseSourceResultFactories = false;
private volatile TransformerConfigurer transformerConfigurer;
public XsltPayloadTransformer(Templates templates) throws ParserConfigurationException {
@@ -141,32 +146,34 @@ public class XsltPayloadTransformer extends AbstractPayloadTransformer<Object, O
this.alwaysUseSourceResultFactories = alwaysUserSourceResultFactories;
}
@Override
public Object transformPayload(Object payload) throws TransformerException {
@Override
protected Object doTransform(Message<?> message) throws Exception {
Transformer transformer = buildTransformer(message);
Object payload = message.getPayload();
Object transformedPayload = null;
if (this.alwaysUseSourceResultFactories) {
transformedPayload = transformUsingFactories(payload);
transformedPayload = transformUsingFactories(payload, transformer);
} else if (payload instanceof String) {
transformedPayload = transformString((String) payload);
transformedPayload = transformString((String) payload, transformer);
} else if (payload instanceof Document) {
transformedPayload = transformDocument((Document) payload);
transformedPayload = transformDocument((Document) payload, transformer);
} else if (payload instanceof Source) {
transformedPayload = transformSource((Source) payload, payload);
transformedPayload = transformSource((Source) payload, payload, transformer);
} else {
// fall back to trying factories
transformedPayload = transformUsingFactories(payload);
transformedPayload = transformUsingFactories(payload, transformer);
}
return transformedPayload;
}
protected Object transformUsingFactories(Object payload) throws TransformerException {
protected Object transformUsingFactories(Object payload, Transformer transformer) throws TransformerException {
Source source = sourceFactory.createSource(payload);
return transformSource(source, payload);
return transformSource(source, payload, transformer);
}
protected Object transformSource(Source source, Object payload) throws TransformerException {
protected Object transformSource(Source source, Object payload, Transformer transformer) throws TransformerException {
Result result = resultFactory.createResult(payload);
this.templates.newTransformer().transform(source, result);
transformer.transform(source, result);
if (resultTransformer != null) {
return resultTransformer.transformResult(result);
@@ -174,14 +181,14 @@ public class XsltPayloadTransformer extends AbstractPayloadTransformer<Object, O
return result;
}
protected String transformString(String stringPayload) throws TransformerException {
protected String transformString(String stringPayload, Transformer transformer) throws TransformerException {
StringResult result = new StringResult();
this.templates.newTransformer().transform(
transformer.transform(
new StringSource(stringPayload), result);
return result.toString();
}
protected Document transformDocument(Document documentPayload) throws TransformerException {
protected Document transformDocument(Document documentPayload, Transformer transformer) throws TransformerException {
DOMSource source = new DOMSource(documentPayload);
Result result = resultFactory.createResult(documentPayload);
if (!DOMResult.class.isAssignableFrom(result.getClass())) {
@@ -189,8 +196,19 @@ public class XsltPayloadTransformer extends AbstractPayloadTransformer<Object, O
"Document to Document conversion requires a DOMResult-producing ResultFactory implementation");
}
DOMResult domResult = (DOMResult) result;
this.templates.newTransformer().transform(source, domResult);
transformer.transform(source, domResult);
return (Document) domResult.getNode();
}
protected Transformer buildTransformer(Message<?> message) throws TransformerException{
Transformer transformer = this.templates.newTransformer();
if(this.transformerConfigurer != null){
this.transformerConfigurer.configureTransfomer(message, transformer);
}
return transformer;
}
}

View File

@@ -0,0 +1,149 @@
package org.springframework.integration.xml.transformer;
import static org.junit.Assert.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import javax.xml.transform.ErrorListener;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.URIResolver;
import org.junit.Before;
import org.junit.Test;
import org.springframework.integration.core.Message;
import org.springframework.integration.message.MessageBuilder;
public class DefaultTransformerConfiguerTests {
StubTransformer transformer;
DefaultTransformerConfigurer transformerConfigurer;
@Before
public void setUp(){
this.transformer = new StubTransformer();
this.transformerConfigurer = new DefaultTransformerConfigurer();
}
@Test
public void testSettingParametersAndOutputProperties(){
Message<String> testMessage = MessageBuilder.withPayload("test")
.setHeader("xslt_parameter_headerOne",1)
.setHeader("xslt_parameter_headerTwo", "string")
.setHeader("xslt_output_property_outOne","1")
.setHeader("xslt_output_property_outTwo","2")
.build();
transformerConfigurer.configureTransfomer(testMessage, transformer);
Object paramOne = transformer.getParameter("headerOne");
assertEquals("Wrong value for headerOne parameter",1, paramOne);
Object paramTwo = transformer.getParameter("headerTwo");
assertEquals("Wrong value for headerTwo parameter","string", paramTwo);
String outPropertyOne = transformer.getOutputProperty("outOne");
assertEquals("Wrong value for headerOne parameter","1", outPropertyOne);
String outPropertyTwo = transformer.getOutputProperty("outTwo");
assertEquals("Wrong value for headerTwo parameter","2", outPropertyTwo);
}
@Test(expected = IllegalArgumentException.class)
public void testNonStringOutputPropertyHeader(){
Message<String> testMessage = MessageBuilder.withPayload("test")
.setHeader("xslt_output_property_outOne",12)
.build();
transformerConfigurer.configureTransfomer(testMessage, transformer);
}
private static class StubTransformer extends Transformer{
Map<String,Object> paramterMap = new HashMap<String, Object>();
Map<String, String> outputProperties = new HashMap<String, String>();
@Override
public void clearParameters() {
paramterMap.clear();
}
@Override
public ErrorListener getErrorListener() {
// TODO Auto-generated method stub
return null;
}
@Override
public Properties getOutputProperties() {
// TODO Auto-generated method stub
return null;
}
@Override
public String getOutputProperty(String name)
throws IllegalArgumentException {
return outputProperties.get(name);
}
@Override
public Object getParameter(String name) {
return paramterMap.get(name);
}
@Override
public URIResolver getURIResolver() {
// TODO Auto-generated method stub
return null;
}
@Override
public void setErrorListener(ErrorListener listener)
throws IllegalArgumentException {
// TODO Auto-generated method stub
}
@Override
public void setOutputProperties(Properties oformat) {
// TODO Auto-generated method stub
}
@Override
public void setOutputProperty(String name, String value)
throws IllegalArgumentException {
outputProperties.put(name, value);
}
@Override
public void setParameter(String name, Object value) {
paramterMap.put(name, value);
}
@Override
public void setURIResolver(URIResolver resolver) {
// TODO Auto-generated method stub
}
@Override
public void transform(Source xmlSource, Result outputTarget)
throws TransformerException {
// TODO Auto-generated method stub
}
}
}

View File

@@ -32,7 +32,9 @@ import org.w3c.dom.Document;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ClassPathResource;
import org.springframework.integration.core.Message;
import org.springframework.integration.core.MessagingException;
import org.springframework.integration.message.MessageBuilder;
import org.springframework.integration.xml.util.XmlTestUtil;
import org.springframework.xml.transform.StringSource;
import static org.hamcrest.CoreMatchers.is;
@@ -48,88 +50,92 @@ public class XsltPayloadTransformerTests {
private String outputAsString = "<bob>test</bob>";
@Before
public void setUp() throws Exception {
transformer = new XsltPayloadTransformer(getXslResource());
}
@Test
public void testDocumentAsPayload() throws Exception {
Object transformed = transformer.transformPayload(
XmlTestUtil.getDocumentForString(docAsString));
assertTrue("Wrong return type for document payload",
Document.class.isAssignableFrom(transformed.getClass()));
Object transformed = transformer.doTransform(buildMessage(XmlTestUtil.getDocumentForString(docAsString)));
assertTrue("Wrong return type for document payload", Document.class
.isAssignableFrom(transformed.getClass()));
Document transformedDocument = (Document) transformed;
assertXMLEqual(outputAsString, XmlTestUtil.docToString(transformedDocument));
assertXMLEqual(outputAsString, XmlTestUtil
.docToString(transformedDocument));
}
@Test
public void testSourceAsPayload() throws Exception {
Object transformed = transformer.transformPayload(new StringSource(docAsString));
assertEquals("Wrong return type for source payload",
DOMResult.class, transformed.getClass());
Object transformed = transformer.doTransform(buildMessage(new StringSource(docAsString)));
assertEquals("Wrong return type for source payload", DOMResult.class,
transformed.getClass());
DOMResult result = (DOMResult) transformed;
assertXMLEqual("Document incorrect after transformation",
XmlTestUtil.getDocumentForString(outputAsString),
(Document) result.getNode());
assertXMLEqual("Document incorrect after transformation", XmlTestUtil
.getDocumentForString(outputAsString), (Document) result
.getNode());
}
@Test
public void testStringAsPayload() throws Exception {
Object transformed = transformer.transformPayload(docAsString);
assertEquals("Wrong return type for string payload",
String.class, transformed.getClass());
Object transformed = transformer.doTransform(buildMessage(docAsString));
assertEquals("Wrong return type for string payload", String.class,
transformed.getClass());
String transformedString = (String) transformed;
assertXMLEqual("String incorrect after transform",
outputAsString, transformedString);
assertXMLEqual("String incorrect after transform", outputAsString,
transformedString);
}
@Test
public void testStringAsPayloadUseFactoriesTrue() throws Exception {
transformer.setAlwaysUseSourceResultFactories(true);
Object transformed = transformer.transformPayload(docAsString);
Object transformed = transformer.doTransform(buildMessage(docAsString));
assertEquals("Wrong return type for useFactories true",
DOMResult.class, transformed.getClass());
DOMResult result = (DOMResult) transformed;
assertXMLEqual("Document incorrect after transformation",
XmlTestUtil.getDocumentForString(outputAsString),
(Document) result.getNode());
assertXMLEqual("Document incorrect after transformation", XmlTestUtil
.getDocumentForString(outputAsString), (Document) result
.getNode());
}
@Test
public void testSourceWithResultTransformer() throws Exception {
Integer returnValue = new Integer(13);
transformer = new XsltPayloadTransformer(getXslResource(), new StubResultTransformer(returnValue));
Object transformed = transformer.transformPayload(new StringSource(docAsString));
assertEquals("Wrong value from result conversion", returnValue, transformed);
transformer = new XsltPayloadTransformer(getXslResource(),
new StubResultTransformer(returnValue));
Object transformed = transformer.doTransform(buildMessage(new StringSource(
docAsString)));
assertEquals("Wrong value from result conversion", returnValue,
transformed);
}
@Test(expected = TransformerException.class)
public void testNonXmlString() throws Exception {
transformer.transformPayload("test");
transformer.doTransform(buildMessage("test"));
}
@Test(expected = MessagingException.class)
public void testUnsupportedPayloadType() throws Exception {
transformer.transformPayload(new Long(12));
transformer.doTransform(buildMessage(new Long(12)));
}
@Test
public void testXsltWithImports() throws Exception {
Resource resource = new ClassPathResource("transform-with-import.xsl", this.getClass());
transformer = new XsltPayloadTransformer(resource);
assertThat(transformer.transformString(docAsString), is(outputAsString));
}
@Test
public void testXsltWithImports() throws Exception {
Resource resource = new ClassPathResource("transform-with-import.xsl",
this.getClass());
transformer = new XsltPayloadTransformer(resource);
assertEquals(transformer.doTransform(buildMessage(docAsString)), outputAsString);
}
protected Message<?> buildMessage(Object payload){
return MessageBuilder.withPayload(payload).build();
}
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"));
}
public static class StubResultTransformer implements ResultTransformer {
private Object objectToReturn;