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://activemq.apache.org/camel/processor.html">Processor</a>
046     * which performs an XSLT transformation of the IN message body
047     *
048     * @version $Revision: 41713 $
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            if (getTemplate() == null) {
071                throw new IllegalArgumentException("No template configured!");
072            }
073            Transformer transformer = getTemplate().newTransformer();
074            configureTransformer(transformer, exchange);
075            Source source = getSource(exchange);
076            ResultHandler resultHandler = resultHandlerFactory.createResult();
077            Result result = resultHandler.getResult();
078    
079            // lets copy the headers before we invoke the transform in case they modify them
080            Message out = exchange.getOut(true);
081            out.copyFrom(exchange.getIn());
082    
083            transformer.transform(source, result);
084            resultHandler.setBody(out);
085        }
086    
087        // Builder methods
088        // -------------------------------------------------------------------------
089    
090        /**
091         * Creates an XSLT processor using the given templates instance
092         */
093        public static XsltBuilder xslt(Templates templates) {
094            return new XsltBuilder(templates);
095        }
096    
097        /**
098         * Creates an XSLT processor using the given XSLT source
099         */
100        public static XsltBuilder xslt(Source xslt) throws TransformerConfigurationException {
101            notNull(xslt, "xslt");
102            XsltBuilder answer = new XsltBuilder();
103            answer.setTransformerSource(xslt);
104            return answer;
105        }
106    
107        /**
108         * Creates an XSLT processor using the given XSLT source
109         */
110        public static XsltBuilder xslt(File xslt) throws TransformerConfigurationException {
111            notNull(xslt, "xslt");
112            return xslt(new StreamSource(xslt));
113        }
114    
115        /**
116         * Creates an XSLT processor using the given XSLT source
117         */
118        public static XsltBuilder xslt(URL xslt) throws TransformerConfigurationException, IOException {
119            notNull(xslt, "xslt");
120            return xslt(xslt.openStream());
121        }
122    
123        /**
124         * Creates an XSLT processor using the given XSLT source
125         */
126        public static XsltBuilder xslt(InputStream xslt) throws TransformerConfigurationException, IOException {
127            notNull(xslt, "xslt");
128            return xslt(new StreamSource(xslt));
129        }
130    
131        /**
132         * Sets the output as being a byte[]
133         */
134        public XsltBuilder outputBytes() {
135            setResultHandlerFactory(new StreamResultHandlerFactory());
136            return this;
137        }
138    
139        /**
140         * Sets the output as being a String
141         */
142        public XsltBuilder outputString() {
143            setResultHandlerFactory(new StringResultHandlerFactory());
144            return this;
145        }
146    
147        /**
148         * Sets the output as being a DOM
149         */
150        public XsltBuilder outputDOM() {
151            setResultHandlerFactory(new DomResultHandlerFactory());
152            return this;
153        }
154    
155        public XsltBuilder parameter(String name, Object value) {
156            parameters.put(name, value);
157            return this;
158        }
159    
160        // Properties
161        // -------------------------------------------------------------------------
162    
163        public Map<String, Object> getParameters() {
164            return parameters;
165        }
166    
167        public void setParameters(Map<String, Object> parameters) {
168            this.parameters = parameters;
169        }
170    
171        public void setTemplate(Templates template) {
172            this.template = template;
173        }
174        
175        public Templates getTemplate() {
176            return template;
177        }
178    
179        public boolean isFailOnNullBody() {
180            return failOnNullBody;
181        }
182    
183        public void setFailOnNullBody(boolean failOnNullBody) {
184            this.failOnNullBody = failOnNullBody;
185        }
186    
187        public ResultHandlerFactory getResultHandlerFactory() {
188            return resultHandlerFactory;
189        }
190    
191        public void setResultHandlerFactory(ResultHandlerFactory resultHandlerFactory) {
192            this.resultHandlerFactory = resultHandlerFactory;
193        }
194    
195        /**
196         * Sets the XSLT transformer from a Source
197         *
198         * @param source  the source
199         * @throws TransformerConfigurationException is thrown if creating a XSLT transformer failed.
200         */
201        public void setTransformerSource(Source source) throws TransformerConfigurationException {
202            // Check that the call to newTemplates() returns a valid template instance.
203            // In case of an xslt parse error, it will return null and we should stop the
204            // deployment and raise an exception as the route will not be setup properly.
205            Templates templates = converter.getTransformerFactory().newTemplates(source);
206            if (templates != null) {
207                setTemplate(templates);
208            } else {
209                throw new TransformerConfigurationException("Error creating XSLT template. "
210                        + "This is most likely be caused by an XML parse error. "
211                        + "Please verify your XSLT file configured.");
212            }
213        }
214    
215        /**
216         * Sets the XSLT transformer from a File
217         */
218        public void setTransformerFile(File xslt) throws TransformerConfigurationException {
219            setTransformerSource(new StreamSource(xslt));
220        }
221    
222        /**
223         * Sets the XSLT transformer from a URL
224         */
225        public void setTransformerURL(URL url) throws TransformerConfigurationException, IOException {
226            notNull(url, "url");
227            setTransformerInputStream(url.openStream());
228        }
229    
230        /**
231         * Sets the XSLT transformer from the given input stream
232         */
233        public void setTransformerInputStream(InputStream in) throws TransformerConfigurationException, IOException {
234            notNull(in, "in");
235            setTransformerSource(new StreamSource(in));
236        }
237    
238        public XmlConverter getConverter() {
239            return converter;
240        }
241    
242        public void setConverter(XmlConverter converter) {
243            this.converter = converter;
244        }
245    
246        // Implementation methods
247        // -------------------------------------------------------------------------
248    
249        /**
250         * Converts the inbound body to a {@link Source}
251         */
252        protected Source getSource(Exchange exchange) {
253            Message in = exchange.getIn();
254            Source source = in.getBody(Source.class);
255            if (source == null) {
256                if (isFailOnNullBody()) {
257                    throw new ExpectedBodyTypeException(exchange, Source.class);
258                } else {
259                    try {
260                        source = converter.toSource(converter.createDocument());
261                    } catch (ParserConfigurationException e) {
262                        throw new RuntimeTransformException(e);
263                    }
264                }
265            }
266            return source;
267        }
268    
269        /**
270         * Configures the transformer with exchange specific parameters
271         */
272        protected void configureTransformer(Transformer transformer, Exchange exchange) {
273            transformer.clearParameters();
274    
275            addParameters(transformer, exchange.getProperties());
276            addParameters(transformer, exchange.getIn().getHeaders());
277            addParameters(transformer, getParameters());
278    
279            transformer.setParameter("exchange", exchange);
280            transformer.setParameter("in", exchange.getIn());
281            transformer.setParameter("out", exchange.getOut());
282        }
283    
284        protected void addParameters(Transformer transformer, Map<String, Object> map) {
285            Set<Map.Entry<String, Object>> propertyEntries = map.entrySet();
286            for (Map.Entry<String, Object> entry : propertyEntries) {
287                String key = entry.getKey();
288                Object value = entry.getValue();
289                if (value != null) {
290                    transformer.setParameter(key, value);
291                }
292            }
293        }
294    }