polishing, also fixed concurrency issue in XsltPayloadTransfomer.buildTransformer() method (assigning rootObject to instance var SpEL EvaluationContext)

This commit is contained in:
Mark Fisher
2010-11-02 18:33:41 -04:00
parent 55941036e4
commit d3cfac25fe
10 changed files with 184 additions and 196 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2009 the original author or authors.
* Copyright 2002-2010 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.
@@ -46,8 +46,7 @@ public class MarshallingTransformer extends AbstractTransformer {
private volatile boolean extractPayload = true;
public MarshallingTransformer(Marshaller marshaller, ResultTransformer resultTransformer)
throws ParserConfigurationException {
public MarshallingTransformer(Marshaller marshaller, ResultTransformer resultTransformer) throws ParserConfigurationException {
Assert.notNull(marshaller, "a marshaller is required");
this.marshaller = marshaller;
this.resultTransformer = resultTransformer;
@@ -89,9 +88,6 @@ public class MarshallingTransformer extends AbstractTransformer {
catch (IOException e) {
throw new MessagingException("Failed to marshal payload", e);
}
if (transformedPayload == null) {
throw new MessagingException("Failed to transform payload");
}
if (this.resultTransformer != null) {
transformedPayload = this.resultTransformer.transformResult(result);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2008 the original author or authors.
* Copyright 2002-2010 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.
@@ -24,11 +24,12 @@ import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Result;
import javax.xml.transform.dom.DOMResult;
import org.springframework.integration.MessagingException;
import org.springframework.xml.transform.StringResult;
import org.w3c.dom.Document;
import org.xml.sax.InputSource;
import org.springframework.integration.MessagingException;
import org.springframework.xml.transform.StringResult;
/**
* Creates a {@link Document} from a {@link Result} payload. Supports
* {@link DOMResult} and {@link StringResult} implementations.
@@ -40,6 +41,7 @@ public class ResultToDocumentTransformer implements ResultTransformer {
// Not guaranteed to be thread safe
private final DocumentBuilderFactory documentBuilderFactory;
public ResultToDocumentTransformer(DocumentBuilderFactory documentBuilderFactory) {
this.documentBuilderFactory = documentBuilderFactory;
}
@@ -49,40 +51,41 @@ public class ResultToDocumentTransformer implements ResultTransformer {
this.documentBuilderFactory.setNamespaceAware(true);
}
public Object transformResult(Result res) {
Document doc = null;
if (DOMResult.class.isAssignableFrom(res.getClass())) {
doc = createDocumentFromDomResult((DOMResult) res);
public Object transformResult(Result result) {
Document document = null;
if (DOMResult.class.isAssignableFrom(result.getClass())) {
document = createDocumentFromDomResult((DOMResult) result);
}
else if (StringResult.class.isAssignableFrom(res.getClass())) {
doc = createDocumentFromStringResult((StringResult) res);
else if (StringResult.class.isAssignableFrom(result.getClass())) {
document = createDocumentFromStringResult((StringResult) result);
}
else {
throw new MessagingException("Failed to create document from payload type [" + res.getClass().getName()
+ "]");
throw new MessagingException("failed to create document from payload type [" +
result.getClass().getName() + "]");
}
return doc;
return document;
}
protected Document createDocumentFromDomResult(DOMResult domResult) {
private Document createDocumentFromDomResult(DOMResult domResult) {
return (Document) domResult.getNode();
}
protected Document createDocumentFromStringResult(StringResult stringResult) {
private Document createDocumentFromStringResult(StringResult stringResult) {
try {
return getDocumentBuilder().parse(new InputSource(new StringReader(stringResult.toString())));
}
catch (Exception e) {
throw new MessagingException("Failed to create Document from StringResult payload", e);
throw new MessagingException("failed to create Document from StringResult payload", e);
}
}
protected synchronized DocumentBuilder getDocumentBuilder() {
private synchronized DocumentBuilder getDocumentBuilder() {
try {
return this.documentBuilderFactory.newDocumentBuilder();
}
catch (ParserConfigurationException e) {
throw new MessagingException("Failed to create a new DocumentBuilder", e);
throw new MessagingException("failed to create a new DocumentBuilder", e);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2007 the original author or authors.
* Copyright 2002-2010 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.
@@ -13,11 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.integration.xml.transformer;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Result;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
@@ -30,62 +28,49 @@ import org.springframework.integration.MessagingException;
import org.springframework.xml.transform.StringResult;
/**
* Converts the passed {@link Result} to an instance of {@link String}
*
* Converts the passed {@link Result} to an instance of {@link String}.
* Supports {@link StringResult} and {@link DOMResult}
*
* @author Jonas Partner
*
* @author Mark Fisher
*/
public class ResultToStringTransformer implements ResultTransformer {
private DocumentBuilderFactory docBuilderFactory;
private final TransformerFactory transformerFactory;
private TransformerFactory transformerFactory;
public ResultToStringTransformer() {
this.docBuilderFactory = DocumentBuilderFactory.newInstance();
this.docBuilderFactory.setNamespaceAware(true);
this.transformerFactory = TransformerFactory.newInstance();
}
protected Transformer getNewTransformer()
throws TransformerConfigurationException {
synchronized (transformerFactory) {
return transformerFactory.newTransformer();
}
}
public Object transformResult(Result res) {
public Object transformResult(Result result) {
String returnString = null;
if (res instanceof StringResult) {
returnString = ((StringResult) res).toString();
} else if (res instanceof DOMResult) {
if (result instanceof StringResult) {
returnString = ((StringResult) result).toString();
}
else if (result instanceof DOMResult) {
try {
StringResult strRes = new StringResult();
getNewTransformer().transform(
new DOMSource(((DOMResult) res).getNode()), strRes);
returnString = strRes.toString();
} catch (TransformerException transE) {
throw new MessagingException(
"Transformation from DOMSOurce failed", transE);
StringResult stringResult = new StringResult();
this.getNewTransformer().transform(
new DOMSource(((DOMResult) result).getNode()), stringResult);
returnString = stringResult.toString();
}
catch (TransformerException e) {
throw new MessagingException("failed to transform from DOMSource failed", e);
}
}
if (returnString == null) {
throw new MessagingException("Could not convert Result type "
+ res.getClass().getName() + " to string");
throw new MessagingException("failed to convert Result type ["
+ result.getClass().getName() + "] to string");
}
return returnString;
}
protected DocumentBuilder getNewDocumentBuilder()
throws ParserConfigurationException {
synchronized (docBuilderFactory) {
return docBuilderFactory.newDocumentBuilder();
private Transformer getNewTransformer() throws TransformerConfigurationException {
synchronized (this.transformerFactory) {
return this.transformerFactory.newTransformer();
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2007 the original author or authors.
* Copyright 2002-2010 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.
@@ -13,10 +13,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.integration.xml.transformer;
import javax.xml.transform.Result;
/**
* @author Jonas Partner
*/
public interface ResultTransformer {
Object transformResult(javax.xml.transform.Result res);
Object transformResult(Result result);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2008 the original author or authors.
* Copyright 2002-2010 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.
@@ -41,6 +41,7 @@ public class SourceCreatingTransformer extends AbstractPayloadTransformer<Object
this.sourceFactory = sourceFactory;
}
@Override
public Source transformPayload(Object payload) {
return this.sourceFactory.createSource(payload);

View File

@@ -50,12 +50,12 @@ import org.springframework.xml.transform.StringSource;
*/
public class UnmarshallingTransformer extends AbstractPayloadTransformer<Object, Object> {
private volatile boolean alwaysUseSourceFactory = false;
private final Unmarshaller unmarshaller;
private volatile SourceFactory sourceFactory = new DomSourceFactory();
private volatile boolean alwaysUseSourceFactory = false;
public UnmarshallingTransformer(Unmarshaller unmarshaller) {
this.unmarshaller = unmarshaller;
@@ -63,22 +63,21 @@ public class UnmarshallingTransformer extends AbstractPayloadTransformer<Object,
/**
* If true always delegate to the {@link SourceFactory}.
*
* @param alwaysUseSourceFactory
*/
public void setAlwaysUseSourceFactory(boolean alwaysUseSourceFactory) {
this.alwaysUseSourceFactory = alwaysUseSourceFactory;
}
/**
* @param sourceFactory
* Provide the SourceFactory to be used. Must not be null.
*/
public void setSourceFactory(SourceFactory sourceFactory) {
Assert.notNull(sourceFactory, "sourceFactory must not be null");
this.sourceFactory = sourceFactory;
}
/**
* If true always delegate to the {@link SourceFactory}.
*/
public void setAlwaysUseSourceFactory(boolean alwaysUseSourceFactory) {
this.alwaysUseSourceFactory = alwaysUseSourceFactory;
}
@Override
public Object transformPayload(Object payload) {
Source source = null;

View File

@@ -31,7 +31,7 @@ import org.springframework.xml.xpath.XPathExpressionFactory;
/**
* Transformer implementation that evaluates XPath expressions against the
* message payload and inserts the result of the evaluation into a messsage
* message payload and inserts the result of the evaluation into a message
* header. The header names will match the keys in the map of expressions.
*
* @author Jonas Partner
@@ -49,7 +49,7 @@ public class XPathHeaderEnricher extends HeaderEnricher {
}
public static class XPathExpressionEvaluatingHeaderValueMessageProcessor implements HeaderValueMessageProcessor<Object> {
static class XPathExpressionEvaluatingHeaderValueMessageProcessor implements HeaderValueMessageProcessor<Object> {
private final XPathExpression expression;

View File

@@ -1,35 +0,0 @@
/*
* Copyright 2002-2010 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 PARAMETER = PREFIX + "parameter_";
}

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.integration.xml.transformer;
import java.io.IOException;
@@ -44,6 +45,7 @@ import org.springframework.integration.xml.result.ResultFactory;
import org.springframework.integration.xml.source.DomSourceFactory;
import org.springframework.integration.xml.source.SourceFactory;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.PatternMatchUtils;
import org.springframework.xml.transform.StringResult;
import org.springframework.xml.transform.StringSource;
@@ -76,8 +78,11 @@ import org.w3c.dom.Document;
public class XsltPayloadTransformer extends AbstractTransformer {
private final Log logger = LogFactory.getLog(this.getClass());
private final Templates templates;
private final StandardEvaluationContext context = new StandardEvaluationContext();
private final StandardEvaluationContext evaluationContext = new StandardEvaluationContext();
private Map<String, Expression> xslParameterMappings;
private final ResultTransformer resultTransformer;
@@ -88,17 +93,13 @@ public class XsltPayloadTransformer extends AbstractTransformer {
private volatile boolean alwaysUseSourceResultFactories = false;
private String[] xsltParamHeaders;
private volatile String[] xsltParamHeaders;
public XsltPayloadTransformer(Templates templates) throws ParserConfigurationException {
this(templates, null);
}
public XsltPayloadTransformer(Templates templates, ResultTransformer resultTransformer) throws ParserConfigurationException {
this.templates = templates;
this.resultTransformer = resultTransformer;
}
public XsltPayloadTransformer(Resource xslResource) throws Exception {
this(TransformerFactory.newInstance().newTemplates(createStreamSourceOnResource(xslResource)), null);
}
@@ -107,28 +108,18 @@ public class XsltPayloadTransformer extends AbstractTransformer {
this(TransformerFactory.newInstance().newTemplates(createStreamSourceOnResource(xslResource)), resultTransformer);
}
/**
* Compensate for the fact that a Resource <i>may</i> not be a File or even
* addressable through a URI. If it is, we want the created StreamSource to
* read other resources relative to the provided one. If it isn't, it loads
* from the default path.
*/
private static StreamSource createStreamSourceOnResource(Resource xslResource) throws IOException {
try {
String systemId = xslResource.getURI().toString();
return new StreamSource(xslResource.getInputStream(), systemId);
}
catch (IOException e) {
return new StreamSource(xslResource.getInputStream());
}
public XsltPayloadTransformer(Templates templates, ResultTransformer resultTransformer) throws ParserConfigurationException {
this.templates = templates;
this.resultTransformer = resultTransformer;
this.evaluationContext.addPropertyAccessor(new MapAccessor());
}
/**
* Sets the SourceFactory.
*/
public void setSourceFactory(SourceFactory sourceFactory) {
Assert.notNull(sourceFactory, "SourceFactory can not be null");
Assert.notNull(sourceFactory, "SourceFactory must not be null");
this.sourceFactory = sourceFactory;
}
@@ -136,7 +127,7 @@ public class XsltPayloadTransformer extends AbstractTransformer {
* Sets the ResultFactory
*/
public void setResultFactory(ResultFactory resultFactory) {
Assert.notNull(sourceFactory, "ResultFactory can not be null");
Assert.notNull(sourceFactory, "ResultFactory must not be null");
this.resultFactory = resultFactory;
}
@@ -144,8 +135,20 @@ public class XsltPayloadTransformer extends AbstractTransformer {
* Specifies whether {@link ResultFactory} and {@link SourceFactory} should always
* be used, even for directly supported payloads such as {@link String} and {@link Document}.
*/
public void setAlwaysUseSourceResultFactories(boolean alwaysUserSourceResultFactories) {
this.alwaysUseSourceResultFactories = alwaysUserSourceResultFactories;
public void setAlwaysUseSourceResultFactories(boolean alwaysUseSourceResultFactories) {
this.alwaysUseSourceResultFactories = alwaysUseSourceResultFactories;
}
public void setXslParameterMappings(Map<String, Expression> xslParameterMappings) {
this.xslParameterMappings = xslParameterMappings;
}
public void setXsltParamHeaders(String[] xsltParamHeaders) {
this.xsltParamHeaders = xsltParamHeaders;
}
public String getComponentType() {
return "xml:xslt-transformer";
}
@Override
@@ -172,29 +175,29 @@ public class XsltPayloadTransformer extends AbstractTransformer {
return transformedPayload;
}
protected Object transformUsingFactories(Object payload, Transformer transformer) throws TransformerException {
Source source = sourceFactory.createSource(payload);
private Object transformUsingFactories(Object payload, Transformer transformer) throws TransformerException {
Source source = this.sourceFactory.createSource(payload);
return transformSource(source, payload, transformer);
}
protected Object transformSource(Source source, Object payload, Transformer transformer) throws TransformerException {
Result result = resultFactory.createResult(payload);
private Object transformSource(Source source, Object payload, Transformer transformer) throws TransformerException {
Result result = this.resultFactory.createResult(payload);
transformer.transform(source, result);
if (resultTransformer != null) {
return resultTransformer.transformResult(result);
if (this.resultTransformer != null) {
return this.resultTransformer.transformResult(result);
}
return result;
}
protected String transformString(String stringPayload, Transformer transformer) throws TransformerException {
private String transformString(String stringPayload, Transformer transformer) throws TransformerException {
StringResult result = new StringResult();
transformer.transform(new StringSource(stringPayload), result);
return result.toString();
}
protected Document transformDocument(Document documentPayload, Transformer transformer) throws TransformerException {
private Document transformDocument(Document documentPayload, Transformer transformer) throws TransformerException {
DOMSource source = new DOMSource(documentPayload);
Result result = resultFactory.createResult(documentPayload);
Result result = this.resultFactory.createResult(documentPayload);
if (!DOMResult.class.isAssignableFrom(result.getClass())) {
throw new MessagingException(
"Document to Document conversion requires a DOMResult-producing ResultFactory implementation.");
@@ -203,51 +206,52 @@ public class XsltPayloadTransformer extends AbstractTransformer {
transformer.transform(source, domResult);
return (Document) domResult.getNode();
}
protected Transformer buildTransformer(Message<?> message) throws TransformerException {
private Transformer buildTransformer(Message<?> message) throws TransformerException {
//process individual mappings
Transformer transformer = this.templates.newTransformer();
context.setRootObject(message);
context.addPropertyAccessor(new MapAccessor());
if (xslParameterMappings != null){
for (String parameterName: xslParameterMappings.keySet()) {
Expression expression = xslParameterMappings.get(parameterName);
Object value = null;
if (this.xslParameterMappings != null){
for (String parameterName: this.xslParameterMappings.keySet()) {
Expression expression = this.xslParameterMappings.get(parameterName);
try {
value = expression.getValue(context);
Object value = expression.getValue(this.evaluationContext, message);
transformer.setParameter(parameterName, value);
} catch (Exception e) {
logger.warn("Header expression '" + expression.getExpressionString() + "' can not resolve within current message and will not be mapped to XSLT parameter");
}
}
catch (Exception e) {
if (logger.isWarnEnabled()) {
logger.warn("Evaluation of header expression '" + expression.getExpressionString() +
"' failed. The XSLT parameter '" + parameterName + "' will be skipped.");
}
}
}
}
// process xslt-parameter-headers
MessageHeaders headers = message.getHeaders();
if (xsltParamHeaders != null){
if (!ObjectUtils.isEmpty(this.xsltParamHeaders)) {
for (String headerName : headers.keySet()) {
if (PatternMatchUtils.simpleMatch(xsltParamHeaders, headerName)){
if (PatternMatchUtils.simpleMatch(this.xsltParamHeaders, headerName)) {
transformer.setParameter(headerName, headers.get(headerName));
}
}
}
}
return transformer;
}
public Map<String, Expression> getXslParameterMappings() {
return xslParameterMappings;
/**
* Compensate for the fact that a Resource <i>may</i> not be a File or even
* addressable through a URI. If it is, we want the created StreamSource to
* read other resources relative to the provided one. If it isn't, it loads
* from the default path.
*/
private static StreamSource createStreamSourceOnResource(Resource xslResource) throws IOException {
try {
String systemId = xslResource.getURI().toString();
return new StreamSource(xslResource.getInputStream(), systemId);
}
catch (IOException e) {
return new StreamSource(xslResource.getInputStream());
}
}
public void setXslParameterMappings(Map<String, Expression> xslParameterMappings) {
this.xslParameterMappings = xslParameterMappings;
}
public String[] getXsltParamHeaders() {
return xsltParamHeaders;
}
public void setXsltParamHeaders(String[] xsltParamHeaders) {
this.xsltParamHeaders = xsltParamHeaders;
}
public String getComponentType(){
return "xml:xslt-transformer";
}
}

View File

@@ -1,33 +1,62 @@
/*
* Copyright 2002-2010 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.xpath;
import org.springframework.xml.xpath.XPathExpression;
import org.w3c.dom.Node;
import org.springframework.xml.xpath.XPathExpression;
/**
* Enumeration of different types o XPath evaluation used to indicate the type of evaluation that should be carried out
* using a provided XPath expression
* Enumeration of different types of XPath evaluation used to indicate the type
* of evaluation that should be carried out using a provided XPath expression.
*/
public enum XPathEvaluationType {
BOOLEAN_RESULT {public Object evaluateXPath(XPathExpression expression, Node node) {
return expression.evaluateAsBoolean(node);
}},
BOOLEAN_RESULT {
public Object evaluateXPath(XPathExpression expression, Node node) {
return expression.evaluateAsBoolean(node);
}
},
STRING_RESULT {public Object evaluateXPath(XPathExpression expression, Node node) {
return expression.evaluateAsString(node);
}},
NUMBER_RESULT {public Object evaluateXPath(XPathExpression expression, Node node) {
return expression.evaluateAsNumber(node);
}},
STRING_RESULT {
public Object evaluateXPath(XPathExpression expression, Node node) {
return expression.evaluateAsString(node);
}
},
NODE_RESULT {public Object evaluateXPath(XPathExpression expression, Node node) {
return expression.evaluateAsNode(node);
}},
NODE_LIST_RESULT {public Object evaluateXPath(XPathExpression expression, Node node) {
return expression.evaluateAsNodeList(node);
}};
NUMBER_RESULT {
public Object evaluateXPath(XPathExpression expression, Node node) {
return expression.evaluateAsNumber(node);
}
},
public abstract Object evaluateXPath(XPathExpression expression, Node node);
NODE_RESULT {
public Object evaluateXPath(XPathExpression expression, Node node) {
return expression.evaluateAsNode(node);
}
},
NODE_LIST_RESULT {
public Object evaluateXPath(XPathExpression expression, Node node) {
return expression.evaluateAsNodeList(node);
}
};
public abstract Object evaluateXPath(XPathExpression expression, Node node);
}