001    /**
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    package org.apache.camel.builder.xml;
018    
019    import java.io.File;
020    import java.io.IOException;
021    import java.io.InputStream;
022    import java.net.URL;
023    import java.util.HashMap;
024    import java.util.Map;
025    import java.util.Set;
026    
027    import javax.xml.parsers.ParserConfigurationException;
028    import javax.xml.transform.Result;
029    import javax.xml.transform.Source;
030    import javax.xml.transform.Templates;
031    import javax.xml.transform.Transformer;
032    import javax.xml.transform.TransformerConfigurationException;
033    import javax.xml.transform.stream.StreamSource;
034    
035    import org.apache.camel.Exchange;
036    import org.apache.camel.ExpectedBodyTypeException;
037    import org.apache.camel.Message;
038    import org.apache.camel.Processor;
039    import org.apache.camel.RuntimeTransformException;
040    import org.apache.camel.converter.jaxp.XmlConverter;
041    
042    import static org.apache.camel.util.ObjectHelper.notNull;
043    
044    /**
045     * Creates a <a href="http://camel.apache.org/processor.html">Processor</a>
046     * which performs an XSLT transformation of the IN message body
047     *
048     * @version $Revision: 3399 $
049     */
050    public class XsltBuilder implements Processor {
051        private Map<String, Object> parameters = new HashMap<String, Object>();
052        private XmlConverter converter = new XmlConverter();
053        private Templates template;
054        private ResultHandlerFactory resultHandlerFactory = new StringResultHandlerFactory();
055        private boolean failOnNullBody = true;
056    
057        public XsltBuilder() {
058        }
059    
060        public XsltBuilder(Templates templates) {
061            this.template = templates;
062        }
063    
064        @Override
065        public String toString() {
066            return "XSLT[" + template + "]";
067        }
068    
069        public void process(Exchange exchange) throws Exception {
070            notNull(getTemplate(), "template");
071    
072            Transformer transformer = getTemplate().newTransformer();
073            configureTransformer(transformer, exchange);
074            Source source = getSource(exchange);
075            ResultHandler resultHandler = resultHandlerFactory.createResult();
076            Result result = resultHandler.getResult();
077    
078            // lets copy the headers before we invoke the transform in case they modify them
079            Message out = exchange.getOut();
080            out.copyFrom(exchange.getIn());
081    
082            transformer.transform(source, result);
083            resultHandler.setBody(out);
084        }
085    
086        // Builder methods
087        // -------------------------------------------------------------------------
088    
089        /**
090         * Creates an XSLT processor using the given templates instance
091         */
092        public static XsltBuilder xslt(Templates templates) {
093            return new XsltBuilder(templates);
094        }
095    
096        /**
097         * Creates an XSLT processor using the given XSLT source
098         */
099        public static XsltBuilder xslt(Source xslt) throws TransformerConfigurationException {
100            notNull(xslt, "xslt");
101            XsltBuilder answer = new XsltBuilder();
102            answer.setTransformerSource(xslt);
103            return answer;
104        }
105    
106        /**
107         * Creates an XSLT processor using the given XSLT source
108         */
109        public static XsltBuilder xslt(File xslt) throws TransformerConfigurationException {
110            notNull(xslt, "xslt");
111            return xslt(new StreamSource(xslt));
112        }
113    
114        /**
115         * Creates an XSLT processor using the given XSLT source
116         */
117        public static XsltBuilder xslt(URL xslt) throws TransformerConfigurationException, IOException {
118            notNull(xslt, "xslt");
119            return xslt(xslt.openStream());
120        }
121    
122        /**
123         * Creates an XSLT processor using the given XSLT source
124         */
125        public static XsltBuilder xslt(InputStream xslt) throws TransformerConfigurationException, IOException {
126            notNull(xslt, "xslt");
127            return xslt(new StreamSource(xslt));
128        }
129    
130        /**
131         * Sets the output as being a byte[]
132         */
133        public XsltBuilder outputBytes() {
134            setResultHandlerFactory(new StreamResultHandlerFactory());
135            return this;
136        }
137    
138        /**
139         * Sets the output as being a String
140         */
141        public XsltBuilder outputString() {
142            setResultHandlerFactory(new StringResultHandlerFactory());
143            return this;
144        }
145    
146        /**
147         * Sets the output as being a DOM
148         */
149        public XsltBuilder outputDOM() {
150            setResultHandlerFactory(new DomResultHandlerFactory());
151            return this;
152        }
153    
154        public XsltBuilder parameter(String name, Object value) {
155            parameters.put(name, value);
156            return this;
157        }
158    
159        // Properties
160        // -------------------------------------------------------------------------
161    
162        public Map<String, Object> getParameters() {
163            return parameters;
164        }
165    
166        public void setParameters(Map<String, Object> parameters) {
167            this.parameters = parameters;
168        }
169    
170        public void setTemplate(Templates template) {
171            this.template = template;
172        }
173        
174        public Templates getTemplate() {
175            return template;
176        }
177    
178        public boolean isFailOnNullBody() {
179            return failOnNullBody;
180        }
181    
182        public void setFailOnNullBody(boolean failOnNullBody) {
183            this.failOnNullBody = failOnNullBody;
184        }
185    
186        public ResultHandlerFactory getResultHandlerFactory() {
187            return resultHandlerFactory;
188        }
189    
190        public void setResultHandlerFactory(ResultHandlerFactory resultHandlerFactory) {
191            this.resultHandlerFactory = resultHandlerFactory;
192        }
193    
194        /**
195         * Sets the XSLT transformer from a Source
196         *
197         * @param source  the source
198         * @throws TransformerConfigurationException is thrown if creating a XSLT transformer failed.
199         */
200        public void setTransformerSource(Source source) throws TransformerConfigurationException {
201            // Check that the call to newTemplates() returns a valid template instance.
202            // In case of an xslt parse error, it will return null and we should stop the
203            // deployment and raise an exception as the route will not be setup properly.
204            Templates templates = converter.getTransformerFactory().newTemplates(source);
205            if (templates != null) {
206                setTemplate(templates);
207            } else {
208                throw new TransformerConfigurationException("Error creating XSLT template. "
209                        + "This is most likely be caused by a XML parse error. "
210                        + "Please verify your XSLT file configured.");
211            }
212        }
213    
214        /**
215         * Sets the XSLT transformer from a File
216         */
217        public void setTransformerFile(File xslt) throws TransformerConfigurationException {
218            setTransformerSource(new StreamSource(xslt));
219        }
220    
221        /**
222         * Sets the XSLT transformer from a URL
223         */
224        public void setTransformerURL(URL url) throws TransformerConfigurationException, IOException {
225            notNull(url, "url");
226            setTransformerInputStream(url.openStream());
227        }
228    
229        /**
230         * Sets the XSLT transformer from the given input stream
231         */
232        public void setTransformerInputStream(InputStream in) throws TransformerConfigurationException, IOException {
233            notNull(in, "in");
234            setTransformerSource(new StreamSource(in));
235        }
236    
237        public XmlConverter getConverter() {
238            return converter;
239        }
240    
241        public void setConverter(XmlConverter converter) {
242            this.converter = converter;
243        }
244    
245        // Implementation methods
246        // -------------------------------------------------------------------------
247    
248        /**
249         * Converts the inbound body to a {@link Source}
250         */
251        protected Source getSource(Exchange exchange) {
252            Message in = exchange.getIn();
253            Source source = in.getBody(Source.class);
254            if (source == null) {
255                if (isFailOnNullBody()) {
256                    throw new ExpectedBodyTypeException(exchange, Source.class);
257                } else {
258                    try {
259                        source = converter.toSource(converter.createDocument());
260                    } catch (ParserConfigurationException e) {
261                        throw new RuntimeTransformException(e);
262                    }
263                }
264            }
265            return source;
266        }
267    
268        /**
269         * Configures the transformer with exchange specific parameters
270         */
271        protected void configureTransformer(Transformer transformer, Exchange exchange) {
272            transformer.clearParameters();
273    
274            addParameters(transformer, exchange.getProperties());
275            addParameters(transformer, exchange.getIn().getHeaders());
276            addParameters(transformer, getParameters());
277    
278            transformer.setParameter("exchange", exchange);
279            transformer.setParameter("in", exchange.getIn());
280            transformer.setParameter("out", exchange.getOut());
281        }
282    
283        protected void addParameters(Transformer transformer, Map<String, Object> map) {
284            Set<Map.Entry<String, Object>> propertyEntries = map.entrySet();
285            for (Map.Entry<String, Object> entry : propertyEntries) {
286                String key = entry.getKey();
287                Object value = entry.getValue();
288                if (value != null) {
289                    transformer.setParameter(key, value);
290                }
291            }
292        }
293    }