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: 658768 $
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 }