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.Transformer;
031    import javax.xml.transform.TransformerConfigurationException;
032    import javax.xml.transform.stream.StreamSource;
033    
034    import org.apache.camel.Exchange;
035    import org.apache.camel.ExpectedBodyTypeException;
036    import org.apache.camel.Message;
037    import org.apache.camel.Processor;
038    import org.apache.camel.RuntimeTransformException;
039    import org.apache.camel.converter.jaxp.XmlConverter;
040    
041    import static org.apache.camel.util.ObjectHelper.notNull;
042    
043    /**
044     * Creates a <a href="http://activemq.apache.org/camel/processor.html">Processor</a>
045     * which performs an XSLT transformation of the IN message body
046     *
047     * @version $Revision: 37863 $
048     */
049    public class XsltBuilder implements Processor {
050        private Map<String, Object> parameters = new HashMap<String, Object>();
051        private XmlConverter converter = new XmlConverter();
052        private Transformer transformer;
053        private ResultHandlerFactory resultHandlerFactory = new StringResultHandlerFactory();
054        private boolean failOnNullBody = true;
055    
056        public XsltBuilder() {
057        }
058    
059        public XsltBuilder(Transformer transformer) {
060            this.transformer = transformer;
061        }
062    
063        @Override
064        public String toString() {
065            return "XSLT[" + transformer + "]";
066        }
067    
068        public synchronized void process(Exchange exchange) throws Exception {
069            Transformer transformer = getTransformer();
070            if (transformer == null) {
071                throw new IllegalArgumentException("No transformer configured!");
072            }
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(true);
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 transformer instance
091         */
092        public static XsltBuilder xslt(Transformer transformer) {
093            return new XsltBuilder(transformer);
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 Transformer getTransformer() {
171            return transformer;
172        }
173    
174        public void setTransformer(Transformer transformer) {
175            this.transformer = transformer;
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 newTransformer() returns a valid transformer 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            Transformer transformer = converter.getTransformerFactory().newTransformer(source);
205            if (transformer != null) {
206                setTransformer(transformer);
207            } else {
208                throw new TransformerConfigurationException("Error creating XSLT transformer. "
209                        + "This is most likely be caused by an 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 transformerwith 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    }