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: 738604 $
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(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 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 }