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 }