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 }