org.springframework.integration.xml.transformer.XsltPayloadTransformer.java Source code

Java tutorial

Introduction

Here is the source code for org.springframework.integration.xml.transformer.XsltPayloadTransformer.java

Source

/*
 * Copyright 2002-2013 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.io.IOException;
import java.util.Map;

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;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamSource;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.w3c.dom.Document;

import org.springframework.core.io.Resource;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.integration.Message;
import org.springframework.integration.MessageHeaders;
import org.springframework.integration.MessagingException;
import org.springframework.integration.expression.ExpressionUtils;
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;
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;

/**
 * Thread safe XSLT transformer implementation which returns a transformed
 * {@link Source}, {@link Document}, or {@link String}. If
 * alwaysUseSourceResultFactories is false (default) the following logic occurs
 * <p>
 * {@link String} payload in results in {@link String} payload out
 * <p>
 * {@link Document} payload in results in {@link Document} payload out
 * <p>
 * {@link Source} payload in results in {@link Result} payload out, type will be
 * determined by the {@link ResultFactory}, {@link DomResultFactory} by default.
 * If an instance of {@link ResultTransformer} is registered this will be used
 * to convert the result.
 * <p>
 * If alwaysUseSourceResultFactories is true then the ResultFactory and
 * {@link SourceFactory} will be used to create the {@link Source} from the
 * payload and the {@link Result} to pass into the transformer. An instance of
 * {@link ResultTransformer} can also be provided to convert the Result prior to
 * returning.
 *
 * @author Jonas Partner
 * @author Mark Fisher
 * @author Oleg Zhurakousky
 * @author Artem Bilan
 */
public class XsltPayloadTransformer extends AbstractTransformer {

    private final Log logger = LogFactory.getLog(this.getClass());

    private final Templates templates;

    private volatile StandardEvaluationContext evaluationContext;

    private Map<String, Expression> xslParameterMappings;

    private final ResultTransformer resultTransformer;

    private volatile SourceFactory sourceFactory = new DomSourceFactory();

    private volatile ResultFactory resultFactory = new DomResultFactory();

    private volatile boolean resultFactoryExplicitlySet;

    private volatile boolean alwaysUseSourceFactory = false;

    private volatile boolean alwaysUseResultFactory = false;

    private volatile String[] xsltParamHeaders;

    public XsltPayloadTransformer(Templates templates) throws ParserConfigurationException {
        this(templates, null);
    }

    public XsltPayloadTransformer(Resource xslResource) throws Exception {
        this(TransformerFactory.newInstance().newTemplates(createStreamSourceOnResource(xslResource)), null);
    }

    public XsltPayloadTransformer(Resource xslResource, ResultTransformer resultTransformer) throws Exception {
        this(TransformerFactory.newInstance().newTemplates(createStreamSourceOnResource(xslResource)),
                resultTransformer);
    }

    public XsltPayloadTransformer(Templates templates, ResultTransformer resultTransformer)
            throws ParserConfigurationException {
        this.templates = templates;
        this.resultTransformer = resultTransformer;
    }

    /**
     * Sets the SourceFactory.
     */
    public void setSourceFactory(SourceFactory sourceFactory) {
        Assert.notNull(sourceFactory, "SourceFactory must not be null");
        this.sourceFactory = sourceFactory;
    }

    /**
     * Sets the ResultFactory
     */
    public void setResultFactory(ResultFactory resultFactory) {
        Assert.notNull(sourceFactory, "ResultFactory must not be null");
        this.resultFactory = resultFactory;
        this.resultFactoryExplicitlySet = true;
    }

    /**
     * Specify whether to always use source factory even for directly supported payload types.
     */
    public void setAlwaysUseSourceFactory(boolean alwaysUseSourceFactory) {
        this.alwaysUseSourceFactory = alwaysUseSourceFactory;
    }

    /**
     * Specify whether to always use result factory even for directly supported payload types
     */
    public void setAlwaysUseResultFactory(boolean alwaysUseResultFactory) {
        this.alwaysUseResultFactory = alwaysUseResultFactory;
    }

    public void setXslParameterMappings(Map<String, Expression> xslParameterMappings) {
        this.xslParameterMappings = xslParameterMappings;
    }

    public void setXsltParamHeaders(String[] xsltParamHeaders) {
        this.xsltParamHeaders = xsltParamHeaders;
    }

    @Override
    public String getComponentType() {
        return "xml:xslt-transformer";
    }

    @Override
    protected void onInit() throws Exception {
        super.onInit();
        this.evaluationContext = ExpressionUtils.createStandardEvaluationContext(this.getBeanFactory());
    }

    @Override
    protected Object doTransform(Message<?> message) throws Exception {
        Transformer transformer = buildTransformer(message);
        Object payload;
        if (this.alwaysUseSourceFactory) {
            payload = sourceFactory.createSource(message.getPayload());
        } else {
            payload = message.getPayload();
        }
        Object transformedPayload = null;
        if (this.alwaysUseResultFactory) {
            transformedPayload = transformUsingResultFactory(payload, transformer);
        } else if (payload instanceof String) {
            transformedPayload = transformString((String) payload, transformer);
        } else if (payload instanceof Document) {
            transformedPayload = transformDocument((Document) payload, transformer);
        } else if (payload instanceof Source) {
            transformedPayload = transformSource((Source) payload, payload, transformer);
        } else {
            // fall back to trying factories
            transformedPayload = transformUsingResultFactory(payload, transformer);
        }
        return transformedPayload;
    }

    private Object transformUsingResultFactory(Object payload, Transformer transformer)
            throws TransformerException {
        Source source;
        if (this.alwaysUseSourceFactory) {
            source = this.sourceFactory.createSource(payload);
        } else if (payload instanceof String) {
            source = new StringSource((String) payload);
        } else if (payload instanceof Document) {
            source = new DOMSource((Document) payload);
        } else if (payload instanceof Source) {
            source = (Source) payload;
        } else {
            source = this.sourceFactory.createSource(payload);
        }
        return transformSource(source, payload, transformer);
    }

    private Object transformSource(Source source, Object payload, Transformer transformer)
            throws TransformerException {
        Result result;
        if (!this.resultFactoryExplicitlySet
                && "text".equals(transformer.getOutputProperties().getProperty("method"))) {
            result = new StringResult();
        } else {
            result = this.resultFactory.createResult(payload);
        }
        transformer.transform(source, result);
        if (this.resultTransformer != null) {
            return this.resultTransformer.transformResult(result);
        }
        return result;
    }

    private String transformString(String stringPayload, Transformer transformer) throws TransformerException {
        StringResult result = new StringResult();
        Source source;
        if (this.alwaysUseSourceFactory) {
            source = this.sourceFactory.createSource(stringPayload);
        } else {
            source = new StringSource(stringPayload);
        }
        transformer.transform(source, result);
        return result.toString();
    }

    private Document transformDocument(Document documentPayload, Transformer transformer)
            throws TransformerException {
        Source source;
        if (this.alwaysUseSourceFactory) {
            source = this.sourceFactory.createSource(documentPayload);
        } else {
            source = new DOMSource(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.");
        }
        DOMResult domResult = (DOMResult) result;
        transformer.transform(source, domResult);
        return (Document) domResult.getNode();
    }

    private Transformer buildTransformer(Message<?> message) throws TransformerException {
        // process individual mappings
        Transformer transformer = this.templates.newTransformer();
        if (this.xslParameterMappings != null) {
            for (String parameterName : this.xslParameterMappings.keySet()) {
                Expression expression = this.xslParameterMappings.get(parameterName);
                try {
                    Object value = expression.getValue(this.evaluationContext, message);
                    transformer.setParameter(parameterName, value);
                } 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 (!ObjectUtils.isEmpty(this.xsltParamHeaders)) {
            for (String headerName : headers.keySet()) {
                if (PatternMatchUtils.simpleMatch(this.xsltParamHeaders, headerName)) {
                    transformer.setParameter(headerName, headers.get(headerName));
                }
            }
        }
        return transformer;
    }

    /**
     * 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());
        }
    }

}