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 */ 017package org.apache.camel.converter.jaxp; 018 019import java.io.ByteArrayInputStream; 020import java.io.ByteArrayOutputStream; 021import java.io.File; 022import java.io.FileInputStream; 023import java.io.FileNotFoundException; 024import java.io.IOException; 025import java.io.InputStream; 026import java.io.InputStreamReader; 027import java.io.Reader; 028import java.io.StringReader; 029import java.io.StringWriter; 030import java.nio.ByteBuffer; 031import java.util.ArrayList; 032import java.util.List; 033import java.util.Map; 034import java.util.Properties; 035 036import javax.xml.parsers.DocumentBuilder; 037import javax.xml.parsers.DocumentBuilderFactory; 038import javax.xml.parsers.ParserConfigurationException; 039import javax.xml.parsers.SAXParserFactory; 040import javax.xml.stream.XMLStreamException; 041import javax.xml.stream.XMLStreamReader; 042import javax.xml.transform.OutputKeys; 043import javax.xml.transform.Result; 044import javax.xml.transform.Source; 045import javax.xml.transform.Transformer; 046import javax.xml.transform.TransformerConfigurationException; 047import javax.xml.transform.TransformerException; 048import javax.xml.transform.TransformerFactory; 049import javax.xml.transform.TransformerFactoryConfigurationError; 050import javax.xml.transform.dom.DOMResult; 051import javax.xml.transform.dom.DOMSource; 052import javax.xml.transform.sax.SAXSource; 053import javax.xml.transform.stax.StAXSource; 054import javax.xml.transform.stream.StreamResult; 055import javax.xml.transform.stream.StreamSource; 056 057import org.w3c.dom.Document; 058import org.w3c.dom.Element; 059import org.w3c.dom.Node; 060import org.w3c.dom.NodeList; 061 062import org.xml.sax.InputSource; 063import org.xml.sax.SAXException; 064import org.xml.sax.XMLReader; 065 066import org.apache.camel.BytesSource; 067import org.apache.camel.Converter; 068import org.apache.camel.Exchange; 069import org.apache.camel.StringSource; 070import org.apache.camel.util.IOHelper; 071import org.apache.camel.util.ObjectHelper; 072import org.slf4j.Logger; 073import org.slf4j.LoggerFactory; 074 075/** 076 * A helper class to transform to and from various JAXB types such as {@link Source} and {@link Document} 077 * 078 * @version 079 */ 080@Converter 081public class XmlConverter { 082 @Deprecated 083 //It will be removed in Camel 3.0, please use the Exchange.DEFAULT_CHARSET 084 public static final String DEFAULT_CHARSET_PROPERTY = "org.apache.camel.default.charset"; 085 086 public static final String OUTPUT_PROPERTIES_PREFIX = "org.apache.camel.xmlconverter.output."; 087 public static final String DOCUMENT_BUILDER_FACTORY_FEATURE = "org.apache.camel.xmlconverter.documentBuilderFactory.feature"; 088 public static String defaultCharset = ObjectHelper.getSystemProperty(Exchange.DEFAULT_CHARSET_PROPERTY, "UTF-8"); 089 090 private static final String JDK_FALLBACK_TRANSFORMER_FACTORY = "com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl"; 091 private static final String XALAN_TRANSFORMER_FACTORY = "org.apache.xalan.processor.TransformerFactoryImpl"; 092 private static final Logger LOG = LoggerFactory.getLogger(XmlConverter.class); 093 094 private volatile DocumentBuilderFactory documentBuilderFactory; 095 private volatile TransformerFactory transformerFactory; 096 private volatile XMLReaderPool xmlReaderPool; 097 098 public XmlConverter() { 099 } 100 101 public XmlConverter(DocumentBuilderFactory documentBuilderFactory) { 102 this.documentBuilderFactory = documentBuilderFactory; 103 } 104 105 /** 106 * Returns the default set of output properties for conversions. 107 */ 108 public Properties defaultOutputProperties() { 109 Properties properties = new Properties(); 110 properties.put(OutputKeys.ENCODING, defaultCharset); 111 properties.put(OutputKeys.OMIT_XML_DECLARATION, "yes"); 112 return properties; 113 } 114 115 /** 116 * Converts the given input Source into the required result 117 */ 118 public void toResult(Source source, Result result) throws TransformerException { 119 toResult(source, result, defaultOutputProperties()); 120 } 121 122 /** 123 * Converts the given input Source into the required result 124 */ 125 public void toResult(Source source, Result result, Properties outputProperties) throws TransformerException { 126 if (source == null) { 127 return; 128 } 129 130 Transformer transformer = createTransformer(); 131 if (transformer == null) { 132 throw new TransformerException("Could not create a transformer - JAXP is misconfigured!"); 133 } 134 transformer.setOutputProperties(outputProperties); 135 if (this.transformerFactory.getClass().getName().equals(XALAN_TRANSFORMER_FACTORY) 136 && (source instanceof StAXSource)) { 137 //external xalan can't handle StAXSource, so convert StAXSource to SAXSource. 138 source = new StAX2SAXSource(((StAXSource) source).getXMLStreamReader()); 139 } 140 transformer.transform(source, result); 141 } 142 143 /** 144 * Converts the given NodeList to a boolean 145 */ 146 @Converter 147 public Boolean toBoolean(NodeList list) { 148 return list.getLength() > 0; 149 } 150 151 /** 152 * Converts the given byte[] to a Source 153 */ 154 @Converter 155 public BytesSource toBytesSource(byte[] data) { 156 return new BytesSource(data); 157 } 158 159 /** 160 * Converts the given String to a Source 161 */ 162 @Converter 163 public StringSource toStringSource(String data) { 164 return new StringSource(data); 165 } 166 167 /** 168 * Converts the given Document to a Source 169 * @deprecated use toDOMSource instead 170 */ 171 @Deprecated 172 public DOMSource toSource(Document document) { 173 return new DOMSource(document); 174 } 175 176 /** 177 * Converts the given Node to a Source 178 * @throws TransformerException 179 * @throws ParserConfigurationException 180 * @deprecated use toDOMSource instead 181 */ 182 @Deprecated 183 public Source toSource(Node node) throws ParserConfigurationException, TransformerException { 184 return toDOMSource(node); 185 } 186 187 /** 188 * Converts the given Node to a Source 189 * @throws TransformerException 190 * @throws ParserConfigurationException 191 */ 192 @Converter 193 public DOMSource toDOMSource(Node node) throws ParserConfigurationException, TransformerException { 194 Document document = toDOMDocument(node); 195 return new DOMSource(document); 196 } 197 198 /** 199 * Converts the given Document to a DOMSource 200 */ 201 @Converter 202 public DOMSource toDOMSource(Document document) { 203 return new DOMSource(document); 204 } 205 206 /** 207 * Converts the given String to a Source 208 */ 209 @Converter 210 public Source toSource(String data) { 211 return new StringSource(data); 212 } 213 214 /** 215 * Converts the given input Source into text. 216 * 217 * @deprecated will be removed in Camel 3.0. Use the method which has 2 parameters. 218 */ 219 @Deprecated 220 public String toString(Source source) throws TransformerException { 221 return toString(source, null); 222 } 223 224 /** 225 * Converts the given input Source into text 226 */ 227 @Converter 228 public String toString(Source source, Exchange exchange) throws TransformerException { 229 if (source == null) { 230 return null; 231 } else if (source instanceof StringSource) { 232 return ((StringSource) source).getText(); 233 } else if (source instanceof BytesSource) { 234 return new String(((BytesSource) source).getData()); 235 } else { 236 StringWriter buffer = new StringWriter(); 237 if (exchange != null) { 238 // check the camelContext properties first 239 Properties properties = ObjectHelper.getCamelPropertiesWithPrefix(OUTPUT_PROPERTIES_PREFIX, exchange.getContext()); 240 if (properties.size() > 0) { 241 toResult(source, new StreamResult(buffer), properties); 242 return buffer.toString(); 243 } 244 } 245 // using the old way to deal with it 246 toResult(source, new StreamResult(buffer)); 247 return buffer.toString(); 248 } 249 } 250 251 /** 252 * Converts the given input Source into bytes 253 */ 254 @Converter 255 public byte[] toByteArray(Source source, Exchange exchange) throws TransformerException { 256 if (source instanceof BytesSource) { 257 return ((BytesSource)source).getData(); 258 } else { 259 ByteArrayOutputStream buffer = new ByteArrayOutputStream(); 260 if (exchange != null) { 261 // check the camelContext properties first 262 Properties properties = ObjectHelper.getCamelPropertiesWithPrefix(OUTPUT_PROPERTIES_PREFIX, 263 exchange.getContext()); 264 if (properties.size() > 0) { 265 toResult(source, new StreamResult(buffer), properties); 266 return buffer.toByteArray(); 267 } 268 } 269 // using the old way to deal with it 270 toResult(source, new StreamResult(buffer)); 271 return buffer.toByteArray(); 272 } 273 } 274 275 /** 276 * Converts the given input Node into text 277 * 278 * @deprecated will be removed in Camel 3.0. Use the method which has 2 parameters. 279 */ 280 @Deprecated 281 public String toString(Node node) throws TransformerException { 282 return toString(node, null); 283 } 284 285 /** 286 * Converts the given input Node into text 287 */ 288 @Converter 289 public String toString(Node node, Exchange exchange) throws TransformerException { 290 return toString(new DOMSource(node), exchange); 291 } 292 293 /** 294 * Converts the given Document to into text 295 * @param document The document to convert 296 * @param outputOptions The {@link OutputKeys} properties to control various aspects of the XML output 297 * @return The string representation of the document 298 * @throws TransformerException 299 */ 300 public String toStringFromDocument(Document document, Properties outputOptions) throws TransformerException { 301 if (document == null) { 302 return null; 303 } 304 305 DOMSource source = new DOMSource(document); 306 StringWriter buffer = new StringWriter(); 307 toResult(source, new StreamResult(buffer), outputOptions); 308 return buffer.toString(); 309 } 310 311 /** 312 * Converts the source instance to a {@link DOMSource} or returns null if the conversion is not 313 * supported (making it easy to derive from this class to add new kinds of conversion). 314 * @deprecated will be removed in Camel 3.0. Use the method which has 2 parameters. 315 */ 316 @Deprecated 317 public DOMSource toDOMSource(Source source) throws ParserConfigurationException, IOException, SAXException, TransformerException { 318 return toDOMSource(source, null); 319 } 320 321 /** 322 * Converts the source instance to a {@link DOMSource} or returns null if the conversion is not 323 * supported (making it easy to derive from this class to add new kinds of conversion). 324 */ 325 @Converter 326 public DOMSource toDOMSource(Source source, Exchange exchange) throws ParserConfigurationException, IOException, SAXException, TransformerException { 327 if (source instanceof DOMSource) { 328 return (DOMSource) source; 329 } else if (source instanceof SAXSource) { 330 return toDOMSourceFromSAX((SAXSource) source); 331 } else if (source instanceof StreamSource) { 332 return toDOMSourceFromStream((StreamSource) source, exchange); 333 } else if (source instanceof StAXSource) { 334 return toDOMSourceFromStAX((StAXSource)source); 335 } else { 336 return null; 337 } 338 } 339 340 /** 341 * Converts the source instance to a {@link DOMSource} or returns null if the conversion is not 342 * supported (making it easy to derive from this class to add new kinds of conversion). 343 */ 344 @Converter 345 public DOMSource toDOMSource(String text) throws ParserConfigurationException, IOException, SAXException, TransformerException { 346 Source source = toSource(text); 347 return toDOMSourceFromStream((StreamSource) source); 348 } 349 350 /** 351 * Converts the source instance to a {@link DOMSource} or returns null if the conversion is not 352 * supported (making it easy to derive from this class to add new kinds of conversion). 353 */ 354 @Converter 355 public DOMSource toDOMSource(byte[] bytes) throws IOException, SAXException, ParserConfigurationException { 356 InputStream is = new ByteArrayInputStream(bytes); 357 try { 358 return toDOMSource(is); 359 } finally { 360 IOHelper.close(is); 361 } 362 } 363 364 365 /** 366 * Converts the source instance to a {@link SAXSource} or returns null if the conversion is not 367 * supported (making it easy to derive from this class to add new kinds of conversion). 368 * 369 * @deprecated will be removed in Camel 3.0. Use the method which has 2 parameters. 370 */ 371 @Deprecated 372 public SAXSource toSAXSource(String source) throws IOException, SAXException, TransformerException { 373 return toSAXSource(source, null); 374 } 375 376 /** 377 * Converts the source instance to a {@link SAXSource} or returns null if the conversion is not 378 * supported (making it easy to derive from this class to add new kinds of conversion). 379 */ 380 @Converter 381 public SAXSource toSAXSource(String source, Exchange exchange) throws IOException, SAXException, TransformerException { 382 return toSAXSource(toSource(source), exchange); 383 } 384 385 /** 386 * Converts the source instance to a {@link StAXSource} or returns null if the conversion is not 387 * supported (making it easy to derive from this class to add new kinds of conversion). 388 * @throws XMLStreamException 389 */ 390 @Converter 391 public StAXSource toStAXSource(String source, Exchange exchange) throws XMLStreamException { 392 XMLStreamReader r = new StaxConverter().createXMLStreamReader(new StringReader(source)); 393 return new StAXSource(r); 394 } 395 396 /** 397 * Converts the source instance to a {@link StAXSource} or returns null if the conversion is not 398 * supported (making it easy to derive from this class to add new kinds of conversion). 399 * @throws XMLStreamException 400 */ 401 @Converter 402 public StAXSource toStAXSource(byte[] in, Exchange exchange) throws XMLStreamException { 403 XMLStreamReader r = new StaxConverter().createXMLStreamReader(new ByteArrayInputStream(in), exchange); 404 return new StAXSource(r); 405 } 406 407 /** 408 * Converts the source instance to a {@link SAXSource} or returns null if the conversion is not 409 * supported (making it easy to derive from this class to add new kinds of conversion). 410 * 411 * @deprecated will be removed in Camel 3.0. Use the method which has 2 parameters. 412 */ 413 @Deprecated 414 public SAXSource toSAXSource(InputStream source) throws IOException, SAXException, TransformerException { 415 return toSAXSource(source, null); 416 } 417 418 /** 419 * Converts the source instance to a {@link SAXSource} or returns null if the conversion is not 420 * supported (making it easy to derive from this class to add new kinds of conversion). 421 */ 422 @Converter 423 public SAXSource toSAXSource(InputStream source, Exchange exchange) throws IOException, SAXException, TransformerException { 424 return toSAXSource(toStreamSource(source), exchange); 425 } 426 427 /** 428 * Converts the source instance to a {@link SAXSource} or returns null if the conversion is not 429 * supported (making it easy to derive from this class to add new kinds of conversion). 430 */ 431 @Converter 432 public SAXSource toSAXSource(byte[] in, Exchange exchange) throws IOException, SAXException, TransformerException { 433 return toSAXSource(toStreamSource(in, exchange), exchange); 434 } 435 436 /** 437 * Converts the source instance to a {@link StAXSource} or returns null if the conversion is not 438 * supported (making it easy to derive from this class to add new kinds of conversion). 439 * @throws XMLStreamException 440 */ 441 @Converter 442 public StAXSource toStAXSource(InputStream source, Exchange exchange) throws XMLStreamException { 443 XMLStreamReader r = new StaxConverter().createXMLStreamReader(source, exchange); 444 return new StAXSource(r); 445 } 446 447 /** 448 * Converts the source instance to a {@link SAXSource} or returns null if the conversion is not 449 * supported (making it easy to derive from this class to add new kinds of conversion). 450 */ 451 @Converter 452 public SAXSource toSAXSource(File file, Exchange exchange) throws IOException, SAXException, TransformerException { 453 InputStream is = IOHelper.buffered(new FileInputStream(file)); 454 return toSAXSource(is, exchange); 455 } 456 457 /** 458 * Converts the source instance to a {@link StAXSource} or returns null if the conversion is not 459 * supported (making it easy to derive from this class to add new kinds of conversion). 460 * @throws FileNotFoundException 461 * @throws XMLStreamException 462 */ 463 @Converter 464 public StAXSource toStAXSource(File file, Exchange exchange) throws FileNotFoundException, XMLStreamException { 465 InputStream is = IOHelper.buffered(new FileInputStream(file)); 466 XMLStreamReader r = new StaxConverter().createXMLStreamReader(is, exchange); 467 return new StAXSource(r); 468 } 469 470 /** 471 * Converts the source instance to a {@link SAXSource} or returns null if the conversion is not 472 * supported (making it easy to derive from this class to add new kinds of conversion). 473 * 474 * @deprecated will be removed in Camel 3.0. Use the method which has 2 parameters. 475 */ 476 @Deprecated 477 public SAXSource toSAXSource(Source source) throws IOException, SAXException, TransformerException { 478 return toSAXSource(source, null); 479 } 480 481 /** 482 * Converts the source instance to a {@link SAXSource} or returns null if the conversion is not 483 * supported (making it easy to derive from this class to add new kinds of conversion). 484 */ 485 @Converter 486 public SAXSource toSAXSource(Source source, Exchange exchange) throws IOException, SAXException, TransformerException { 487 if (source instanceof SAXSource) { 488 return (SAXSource) source; 489 } else if (source instanceof DOMSource) { 490 return toSAXSourceFromDOM((DOMSource) source, exchange); 491 } else if (source instanceof StreamSource) { 492 return toSAXSourceFromStream((StreamSource) source, exchange); 493 } else if (source instanceof StAXSource) { 494 return toSAXSourceFromStAX((StAXSource) source, exchange); 495 } else { 496 return null; 497 } 498 } 499 500 /** 501 * @deprecated will be removed in Camel 3.0. Use the method which has 2 parameters. 502 */ 503 @Deprecated 504 public StreamSource toStreamSource(Source source) throws TransformerException { 505 return toStreamSource(source, null); 506 } 507 508 @Converter 509 public StreamSource toStreamSource(Source source, Exchange exchange) throws TransformerException { 510 if (source instanceof StreamSource) { 511 return (StreamSource) source; 512 } else if (source instanceof DOMSource) { 513 return toStreamSourceFromDOM((DOMSource) source, exchange); 514 } else if (source instanceof SAXSource) { 515 return toStreamSourceFromSAX((SAXSource) source, exchange); 516 } else if (source instanceof StAXSource) { 517 return toStreamSourceFromStAX((StAXSource) source, exchange); 518 } else { 519 return null; 520 } 521 } 522 523 @Converter 524 public StreamSource toStreamSource(InputStream in) throws TransformerException { 525 return new StreamSource(in); 526 } 527 528 @Converter 529 public StreamSource toStreamSource(Reader in) throws TransformerException { 530 return new StreamSource(in); 531 } 532 533 @Converter 534 public StreamSource toStreamSource(File in) throws TransformerException { 535 return new StreamSource(in); 536 } 537 538 @Converter 539 public StreamSource toStreamSource(byte[] in, Exchange exchange) throws TransformerException { 540 InputStream is = exchange.getContext().getTypeConverter().convertTo(InputStream.class, exchange, in); 541 return new StreamSource(is); 542 } 543 544 @Converter 545 public StreamSource toStreamSource(ByteBuffer in, Exchange exchange) throws TransformerException { 546 InputStream is = exchange.getContext().getTypeConverter().convertTo(InputStream.class, exchange, in); 547 return new StreamSource(is); 548 } 549 550 /** 551 * @deprecated will be removed in Camel 3.0. Use the method which has 2 parameters. 552 */ 553 @Deprecated 554 public StreamSource toStreamSourceFromSAX(SAXSource source) throws TransformerException { 555 return toStreamSourceFromSAX(source, null); 556 } 557 558 @Converter 559 public StreamSource toStreamSourceFromSAX(SAXSource source, Exchange exchange) throws TransformerException { 560 InputSource inputSource = source.getInputSource(); 561 if (inputSource != null) { 562 if (inputSource.getCharacterStream() != null) { 563 return new StreamSource(inputSource.getCharacterStream()); 564 } 565 if (inputSource.getByteStream() != null) { 566 return new StreamSource(inputSource.getByteStream()); 567 } 568 } 569 String result = toString(source, exchange); 570 return new StringSource(result); 571 } 572 573 /** 574 * @deprecated will be removed in Camel 3.0. Use the method which has 2 parameters. 575 */ 576 @Deprecated 577 public StreamSource toStreamSourceFromDOM(DOMSource source) throws TransformerException { 578 return toStreamSourceFromDOM(source, null); 579 } 580 581 @Converter 582 public StreamSource toStreamSourceFromDOM(DOMSource source, Exchange exchange) throws TransformerException { 583 String result = toString(source, exchange); 584 return new StringSource(result); 585 } 586 @Converter 587 public StreamSource toStreamSourceFromStAX(StAXSource source, Exchange exchange) throws TransformerException { 588 String result = toString(source, exchange); 589 return new StringSource(result); 590 } 591 592 /** 593 * @deprecated will be removed in Camel 3.0. Use the method which has 2 parameters. 594 */ 595 @Deprecated 596 public SAXSource toSAXSourceFromStream(StreamSource source) throws SAXException { 597 return toSAXSourceFromStream(source, null); 598 } 599 600 @Converter 601 public SAXSource toSAXSourceFromStream(StreamSource source, Exchange exchange) throws SAXException { 602 InputSource inputSource; 603 if (source.getReader() != null) { 604 inputSource = new InputSource(source.getReader()); 605 } else { 606 inputSource = new InputSource(source.getInputStream()); 607 } 608 inputSource.setSystemId(source.getSystemId()); 609 inputSource.setPublicId(source.getPublicId()); 610 611 XMLReader xmlReader = null; 612 try { 613 // use the SAXPaserFactory which is set from exchange 614 if (exchange != null) { 615 SAXParserFactory sfactory = exchange.getProperty(Exchange.SAXPARSER_FACTORY, SAXParserFactory.class); 616 if (sfactory != null) { 617 if (!sfactory.isNamespaceAware()) { 618 sfactory.setNamespaceAware(true); 619 } 620 xmlReader = sfactory.newSAXParser().getXMLReader(); 621 } 622 } 623 if (xmlReader == null) { 624 if (xmlReaderPool == null) { 625 xmlReaderPool = new XMLReaderPool(createSAXParserFactory()); 626 } 627 xmlReader = xmlReaderPool.createXMLReader(); 628 } 629 } catch (Exception ex) { 630 LOG.warn("Cannot create the SAXParser XMLReader, due to {}", ex); 631 } 632 return new SAXSource(xmlReader, inputSource); 633 } 634 635 /** 636 * @deprecated will be removed in Camel 3.0. Use the method which has 2 parameters. 637 */ 638 @Deprecated 639 public Reader toReaderFromSource(Source src) throws TransformerException { 640 return toReaderFromSource(src, null); 641 } 642 643 @Converter 644 public Reader toReaderFromSource(Source src, Exchange exchange) throws TransformerException { 645 StreamSource stSrc = toStreamSource(src, exchange); 646 Reader r = stSrc.getReader(); 647 if (r == null) { 648 r = new InputStreamReader(stSrc.getInputStream()); 649 } 650 return r; 651 } 652 653 /** 654 * @deprecated will be removed in Camel 3.0. Use the method which has 2 parameters. 655 */ 656 @Deprecated 657 public DOMSource toDOMSource(InputStream is) throws ParserConfigurationException, IOException, SAXException { 658 return toDOMSource(is, null); 659 } 660 661 @Converter 662 public DOMSource toDOMSource(InputStream is, Exchange exchange) throws ParserConfigurationException, IOException, SAXException { 663 InputSource source = new InputSource(is); 664 String systemId = source.getSystemId(); 665 DocumentBuilder builder = getDocumentBuilderFactory(exchange).newDocumentBuilder(); 666 Document document = builder.parse(source); 667 return new DOMSource(document, systemId); 668 } 669 670 /** 671 * @deprecated will be removed in Camel 3.0. Use the method which has 2 parameters. 672 */ 673 @Deprecated 674 public DOMSource toDOMSource(File file) throws ParserConfigurationException, IOException, SAXException { 675 return toDOMSource(file, null); 676 } 677 678 @Converter 679 public DOMSource toDOMSource(File file, Exchange exchange) throws ParserConfigurationException, IOException, SAXException { 680 InputStream is = IOHelper.buffered(new FileInputStream(file)); 681 return toDOMSource(is, exchange); 682 } 683 684 /** 685 * @deprecated will be removed in Camel 3.0. Use the method which has 2 parameters. 686 */ 687 @Deprecated 688 public DOMSource toDOMSourceFromStream(StreamSource source) throws ParserConfigurationException, IOException, SAXException { 689 return toDOMSourceFromStream(source, null); 690 } 691 692 @Converter 693 public DOMSource toDOMSourceFromStream(StreamSource source, Exchange exchange) throws ParserConfigurationException, IOException, SAXException { 694 Document document; 695 String systemId = source.getSystemId(); 696 697 DocumentBuilder builder = getDocumentBuilderFactory(exchange).newDocumentBuilder(); 698 Reader reader = source.getReader(); 699 if (reader != null) { 700 document = builder.parse(new InputSource(reader)); 701 } else { 702 InputStream inputStream = source.getInputStream(); 703 if (inputStream != null) { 704 InputSource inputsource = new InputSource(inputStream); 705 inputsource.setSystemId(systemId); 706 document = builder.parse(inputsource); 707 } else { 708 throw new IOException("No input stream or reader available on StreamSource: " + source); 709 } 710 } 711 return new DOMSource(document, systemId); 712 } 713 714 /** 715 * @deprecated will be removed in Camel 3.0. Use the method which has 2 parameters. 716 */ 717 @Deprecated 718 public SAXSource toSAXSourceFromDOM(DOMSource source) throws TransformerException { 719 return toSAXSourceFromDOM(source, null); 720 } 721 722 @Converter 723 public SAXSource toSAXSourceFromDOM(DOMSource source, Exchange exchange) throws TransformerException { 724 String str = toString(source, exchange); 725 StringReader reader = new StringReader(str); 726 return new SAXSource(new InputSource(reader)); 727 } 728 729 @Converter 730 public SAXSource toSAXSourceFromStAX(StAXSource source, Exchange exchange) throws TransformerException { 731 String str = toString(source, exchange); 732 StringReader reader = new StringReader(str); 733 return new SAXSource(new InputSource(reader)); 734 } 735 736 @Converter 737 public DOMSource toDOMSourceFromSAX(SAXSource source) throws IOException, SAXException, ParserConfigurationException, TransformerException { 738 return new DOMSource(toDOMNodeFromSAX(source)); 739 } 740 741 @Converter 742 public DOMSource toDOMSourceFromStAX(StAXSource source) throws IOException, SAXException, ParserConfigurationException, TransformerException { 743 return new DOMSource(toDOMNodeFromStAX(source)); 744 } 745 746 @Converter 747 public Node toDOMNodeFromSAX(SAXSource source) throws ParserConfigurationException, IOException, SAXException, TransformerException { 748 DOMResult result = new DOMResult(); 749 toResult(source, result); 750 return result.getNode(); 751 } 752 753 @Converter 754 public Node toDOMNodeFromStAX(StAXSource source) throws ParserConfigurationException, IOException, SAXException, TransformerException { 755 DOMResult result = new DOMResult(); 756 toResult(source, result); 757 return result.getNode(); 758 } 759 760 /** 761 * Convert a NodeList consisting of just 1 node to a DOM Node. 762 * @param nl the NodeList 763 * @return the DOM Node 764 */ 765 @Converter(allowNull = true) 766 public Node toDOMNodeFromSingleNodeList(NodeList nl) { 767 return nl.getLength() == 1 ? nl.item(0) : null; 768 } 769 770 /** 771 * Convert a NodeList consisting of just 1 node to a DOM Document. 772 * Cannot convert NodeList with length > 1 because they require a root node. 773 * @param nl the NodeList 774 * @return the DOM Document 775 */ 776 @Converter(allowNull = true) 777 public Document toDOMDocumentFromSingleNodeList(NodeList nl) throws ParserConfigurationException, TransformerException { 778 if (nl.getLength() == 1) { 779 return toDOMDocument(nl.item(0)); 780 } else if (nl instanceof Node) { 781 // as XML parsers may often have nodes that implement both Node and NodeList then the type converter lookup 782 // may lookup either a type converter from NodeList or Node. So let's fallback and try with Node 783 return toDOMDocument((Node) nl); 784 } else { 785 return null; 786 } 787 } 788 789 /** 790 * Converts the given TRaX Source into a W3C DOM node 791 */ 792 @Converter(allowNull = true) 793 public Node toDOMNode(Source source) throws TransformerException, ParserConfigurationException, IOException, SAXException { 794 DOMSource domSrc = toDOMSource(source); 795 return domSrc != null ? domSrc.getNode() : null; 796 } 797 798 /** 799 * Create a DOM element from the given source. 800 */ 801 @Converter 802 public Element toDOMElement(Source source) throws TransformerException, ParserConfigurationException, IOException, SAXException { 803 Node node = toDOMNode(source); 804 return toDOMElement(node); 805 } 806 807 /** 808 * Create a DOM element from the DOM node. 809 * Simply cast if the node is an Element, or 810 * return the root element if it is a Document. 811 */ 812 @Converter 813 public Element toDOMElement(Node node) throws TransformerException { 814 // If the node is an document, return the root element 815 if (node instanceof Document) { 816 return ((Document) node).getDocumentElement(); 817 // If the node is an element, just cast it 818 } else if (node instanceof Element) { 819 return (Element) node; 820 // Other node types are not handled 821 } else { 822 throw new TransformerException("Unable to convert DOM node to an Element"); 823 } 824 } 825 826 827 /** 828 * Converts the given data to a DOM document 829 * 830 * @param data is the data to be parsed 831 * @return the parsed document 832 */ 833 @Deprecated 834 public Document toDOMDocument(byte[] data) throws IOException, SAXException, ParserConfigurationException { 835 return toDOMDocument(data, null); 836 } 837 838 /** 839 * Converts the given data to a DOM document 840 * 841 * @param data is the data to be parsed 842 * @param exchange is the exchange to be used when calling the converter 843 * @return the parsed document 844 */ 845 @Converter 846 public Document toDOMDocument(byte[] data, Exchange exchange) throws IOException, SAXException, ParserConfigurationException { 847 DocumentBuilder documentBuilder = getDocumentBuilderFactory(exchange).newDocumentBuilder(); 848 return documentBuilder.parse(new ByteArrayInputStream(data)); 849 } 850 851 /** 852 * Converts the given {@link InputStream} to a DOM document 853 * 854 * @param in is the data to be parsed 855 * @return the parsed document 856 * @deprecated will be removed in Camel 3.0. Use the method which has 2 parameters. 857 */ 858 @Deprecated 859 public Document toDOMDocument(InputStream in) throws IOException, SAXException, ParserConfigurationException { 860 return toDOMDocument(in, null); 861 } 862 863 /** 864 * Converts the given {@link InputStream} to a DOM document 865 * 866 * @param in is the data to be parsed 867 * @param exchange is the exchange to be used when calling the converter 868 * @return the parsed document 869 */ 870 @Converter 871 public Document toDOMDocument(InputStream in, Exchange exchange) throws IOException, SAXException, ParserConfigurationException { 872 DocumentBuilder documentBuilder = getDocumentBuilderFactory(exchange).newDocumentBuilder(); 873 return documentBuilder.parse(in); 874 } 875 876 /** 877 * Converts the given {@link InputStream} to a DOM document 878 * 879 * @param in is the data to be parsed 880 * @return the parsed document 881 * @deprecated will be removed in Camel 3.0. Use the method which has 2 parameters. 882 */ 883 @Deprecated 884 public Document toDOMDocument(Reader in) throws IOException, SAXException, ParserConfigurationException { 885 return toDOMDocument(new InputSource(in)); 886 } 887 888 /** 889 * Converts the given {@link InputStream} to a DOM document 890 * 891 * @param in is the data to be parsed 892 * @param exchange is the exchange to be used when calling the converter 893 * @return the parsed document 894 */ 895 @Converter 896 public Document toDOMDocument(Reader in, Exchange exchange) throws IOException, SAXException, ParserConfigurationException { 897 return toDOMDocument(new InputSource(in), exchange); 898 } 899 900 /** 901 * Converts the given {@link InputSource} to a DOM document 902 * 903 * @param in is the data to be parsed 904 * @return the parsed document 905 * @deprecated will be removed in Camel 3.0. Use the method which has 2 parameters. 906 */ 907 @Deprecated 908 public Document toDOMDocument(InputSource in) throws IOException, SAXException, ParserConfigurationException { 909 return toDOMDocument(in, null); 910 } 911 912 /** 913 * Converts the given {@link InputSource} to a DOM document 914 * 915 * @param in is the data to be parsed 916 * @param exchange is the exchange to be used when calling the converter 917 * @return the parsed document 918 */ 919 @Converter 920 public Document toDOMDocument(InputSource in, Exchange exchange) throws IOException, SAXException, ParserConfigurationException { 921 DocumentBuilder documentBuilder = getDocumentBuilderFactory(exchange).newDocumentBuilder(); 922 return documentBuilder.parse(in); 923 } 924 925 /** 926 * Converts the given {@link String} to a DOM document 927 * 928 * @param text is the data to be parsed 929 * @return the parsed document 930 * @deprecated will be removed in Camel 3.0. Use the method which has 2 parameters. 931 */ 932 @Deprecated 933 public Document toDOMDocument(String text) throws IOException, SAXException, ParserConfigurationException { 934 return toDOMDocument(new StringReader(text)); 935 } 936 937 /** 938 * Converts the given {@link String} to a DOM document 939 * 940 * @param text is the data to be parsed 941 * @param exchange is the exchange to be used when calling the converter 942 * @return the parsed document 943 */ 944 @Converter 945 public Document toDOMDocument(String text, Exchange exchange) throws IOException, SAXException, ParserConfigurationException { 946 return toDOMDocument(new StringReader(text), exchange); 947 } 948 949 /** 950 * Converts the given {@link File} to a DOM document 951 * 952 * @param file is the data to be parsed 953 * @return the parsed document 954 * @deprecated will be removed in Camel 3.0. Use the method which has 2 parameters. 955 */ 956 @Deprecated 957 public Document toDOMDocument(File file) throws IOException, SAXException, ParserConfigurationException { 958 return toDOMDocument(file, null); 959 } 960 961 /** 962 * Converts the given {@link File} to a DOM document 963 * 964 * @param file is the data to be parsed 965 * @param exchange is the exchange to be used when calling the converter 966 * @return the parsed document 967 */ 968 @Converter 969 public Document toDOMDocument(File file, Exchange exchange) throws IOException, SAXException, ParserConfigurationException { 970 DocumentBuilder documentBuilder = getDocumentBuilderFactory(exchange).newDocumentBuilder(); 971 return documentBuilder.parse(file); 972 } 973 974 /** 975 * Create a DOM document from the given source. 976 */ 977 @Converter 978 public Document toDOMDocument(Source source) throws TransformerException, ParserConfigurationException, IOException, SAXException { 979 Node node = toDOMNode(source); 980 return toDOMDocument(node); 981 } 982 983 /** 984 * Create a DOM document from the given Node. 985 * 986 * If the node is an document, just cast it, if the node is an root element, retrieve its 987 * owner element or create a new document and import the node. 988 */ 989 @Converter 990 public Document toDOMDocument(final Node node) throws ParserConfigurationException, TransformerException { 991 ObjectHelper.notNull(node, "node"); 992 993 // If the node is the document, just cast it 994 if (node instanceof Document) { 995 return (Document) node; 996 // If the node is an element 997 } else if (node instanceof Element) { 998 Element elem = (Element) node; 999 // If this is the root element, return its owner document 1000 if (elem.getOwnerDocument().getDocumentElement() == elem) { 1001 return elem.getOwnerDocument(); 1002 // else, create a new doc and copy the element inside it 1003 } else { 1004 Document doc = createDocument(); 1005 // import node must not occur concurrent on the same node (must be its owner) 1006 // so we need to synchronize on it 1007 synchronized (node.getOwnerDocument()) { 1008 doc.appendChild(doc.importNode(node, true)); 1009 } 1010 return doc; 1011 } 1012 // other element types are not handled 1013 } else { 1014 throw new TransformerException("Unable to convert DOM node to a Document: " + node); 1015 } 1016 } 1017 1018 /** 1019 * @deprecated will be removed in Camel 3.0. Use the method which has 2 parameters. 1020 */ 1021 @Deprecated 1022 public InputStream toInputStream(DOMSource source) throws TransformerException, IOException { 1023 return toInputStream(source, null); 1024 } 1025 1026 @Converter 1027 public InputStream toInputStream(DOMSource source, Exchange exchange) throws TransformerException, IOException { 1028 return new ByteArrayInputStream(toByteArray(source, exchange)); 1029 } 1030 1031 /** 1032 * @deprecated will be removed in Camel 3.0. Use the method which has 2 parameters. 1033 */ 1034 @Deprecated 1035 public InputStream toInputStream(Document dom) throws TransformerException, IOException { 1036 return toInputStream(dom, null); 1037 } 1038 1039 @Converter 1040 public InputStream toInputStream(Document dom, Exchange exchange) throws TransformerException, IOException { 1041 return toInputStream(new DOMSource(dom), exchange); 1042 } 1043 1044 @Converter 1045 public InputSource toInputSource(InputStream is, Exchange exchange) { 1046 return new InputSource(is); 1047 } 1048 1049 @Converter 1050 public InputSource toInputSource(File file, Exchange exchange) throws FileNotFoundException { 1051 InputStream is = IOHelper.buffered(new FileInputStream(file)); 1052 return new InputSource(is); 1053 } 1054 1055 // Properties 1056 //------------------------------------------------------------------------- 1057 1058 public DocumentBuilderFactory getDocumentBuilderFactory() { 1059 if (documentBuilderFactory == null) { 1060 documentBuilderFactory = createDocumentBuilderFactory(); 1061 } 1062 return documentBuilderFactory; 1063 } 1064 1065 public void setDocumentBuilderFactory(DocumentBuilderFactory documentBuilderFactory) { 1066 this.documentBuilderFactory = documentBuilderFactory; 1067 } 1068 1069 public TransformerFactory getTransformerFactory() { 1070 if (transformerFactory == null) { 1071 transformerFactory = createTransformerFactory(); 1072 } 1073 return transformerFactory; 1074 } 1075 1076 public void setTransformerFactory(TransformerFactory transformerFactory) { 1077 if (transformerFactory != null) { 1078 configureSaxonTransformerFactory(transformerFactory); 1079 } 1080 this.transformerFactory = transformerFactory; 1081 } 1082 1083 // Helper methods 1084 //------------------------------------------------------------------------- 1085 1086 protected void setupFeatures(DocumentBuilderFactory factory) { 1087 Properties properties = System.getProperties(); 1088 List<String> features = new ArrayList<String>(); 1089 for (Map.Entry<Object, Object> prop : properties.entrySet()) { 1090 String key = (String) prop.getKey(); 1091 if (key.startsWith(XmlConverter.DOCUMENT_BUILDER_FACTORY_FEATURE)) { 1092 String uri = ObjectHelper.after(key, ":"); 1093 Boolean value = Boolean.valueOf((String)prop.getValue()); 1094 try { 1095 factory.setFeature(uri, value); 1096 features.add("feature " + uri + " value " + value); 1097 } catch (ParserConfigurationException e) { 1098 LOG.warn("DocumentBuilderFactory doesn't support the feature {} with value {}, due to {}.", new Object[]{uri, value, e}); 1099 } 1100 } 1101 } 1102 if (features.size() > 0) { 1103 StringBuilder featureString = new StringBuilder(); 1104 // just log the configured feature 1105 for (String feature : features) { 1106 if (featureString.length() != 0) { 1107 featureString.append(", "); 1108 } 1109 featureString.append(feature); 1110 } 1111 LOG.info("DocumentBuilderFactory has been set with features {{}}.", featureString.toString()); 1112 } 1113 1114 } 1115 1116 public DocumentBuilderFactory getDocumentBuilderFactory(Exchange exchange) { 1117 DocumentBuilderFactory answer = getDocumentBuilderFactory(); 1118 // Get the DocumentBuilderFactory from the exchange header first 1119 if (exchange != null) { 1120 DocumentBuilderFactory factory = exchange.getProperty(Exchange.DOCUMENT_BUILDER_FACTORY, DocumentBuilderFactory.class); 1121 if (factory != null) { 1122 answer = factory; 1123 } 1124 } 1125 return answer; 1126 } 1127 1128 public DocumentBuilderFactory createDocumentBuilderFactory() { 1129 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 1130 factory.setNamespaceAware(true); 1131 factory.setIgnoringElementContentWhitespace(true); 1132 factory.setIgnoringComments(true); 1133 try { 1134 // Disable the external-general-entities by default 1135 factory.setFeature("http://xml.org/sax/features/external-general-entities", false); 1136 } catch (ParserConfigurationException e) { 1137 LOG.warn("DocumentBuilderFactory doesn't support the feature {} with value {}, due to {}.", 1138 new Object[]{"http://xml.org/sax/features/external-general-entities", false, e}); 1139 } 1140 // setup the SecurityManager by default if it's apache xerces 1141 try { 1142 Class<?> smClass = ObjectHelper.loadClass("org.apache.xerces.util.SecurityManager"); 1143 if (smClass != null) { 1144 Object sm = smClass.newInstance(); 1145 // Here we just use the default setting of the SeurityManager 1146 factory.setAttribute("http://apache.org/xml/properties/security-manager", sm); 1147 } 1148 } catch (Exception e) { 1149 LOG.warn("DocumentBuilderFactory doesn't support the attribute {}, due to {}.", 1150 new Object[]{"http://apache.org/xml/properties/security-manager", e}); 1151 } 1152 // setup the feature from the system property 1153 setupFeatures(factory); 1154 return factory; 1155 } 1156 1157 public DocumentBuilder createDocumentBuilder() throws ParserConfigurationException { 1158 DocumentBuilderFactory factory = getDocumentBuilderFactory(); 1159 return factory.newDocumentBuilder(); 1160 } 1161 1162 public Document createDocument() throws ParserConfigurationException { 1163 DocumentBuilder builder = createDocumentBuilder(); 1164 return builder.newDocument(); 1165 } 1166 1167 /** 1168 * @deprecated use {@link #createTransformer}, will be removed in Camel 3.0 1169 */ 1170 @Deprecated 1171 public Transformer createTransfomer() throws TransformerConfigurationException { 1172 return createTransformer(); 1173 } 1174 1175 public Transformer createTransformer() throws TransformerConfigurationException { 1176 TransformerFactory factory = getTransformerFactory(); 1177 return factory.newTransformer(); 1178 } 1179 1180 public TransformerFactory createTransformerFactory() { 1181 TransformerFactory factory; 1182 TransformerFactoryConfigurationError cause; 1183 try { 1184 factory = TransformerFactory.newInstance(); 1185 } catch (TransformerFactoryConfigurationError e) { 1186 cause = e; 1187 // try fallback from the JDK 1188 try { 1189 LOG.debug("Cannot create/load TransformerFactory due: {}. Will attempt to use JDK fallback TransformerFactory: {}", e.getMessage(), JDK_FALLBACK_TRANSFORMER_FACTORY); 1190 factory = TransformerFactory.newInstance(JDK_FALLBACK_TRANSFORMER_FACTORY, null); 1191 } catch (Throwable t) { 1192 // okay we cannot load fallback then throw original exception 1193 throw cause; 1194 } 1195 } 1196 LOG.debug("Created TransformerFactory: {}", factory); 1197 1198 // Enable the Security feature by default 1199 try { 1200 factory.setFeature(javax.xml.XMLConstants.FEATURE_SECURE_PROCESSING, true); 1201 } catch (TransformerConfigurationException e) { 1202 LOG.warn("TransformerFactory doesn't support the feature {} with value {}, due to {}.", new Object[]{javax.xml.XMLConstants.FEATURE_SECURE_PROCESSING, "true", e}); 1203 } 1204 factory.setErrorListener(new XmlErrorListener()); 1205 configureSaxonTransformerFactory(factory); 1206 return factory; 1207 } 1208 1209 /** 1210 * Make a Saxon TransformerFactory more JAXP compliant by configuring it to 1211 * send <xsl:message> output to the ErrorListener. 1212 * 1213 * @param factory 1214 * the TransformerFactory 1215 */ 1216 public void configureSaxonTransformerFactory(TransformerFactory factory) { 1217 // check whether we have a Saxon TransformerFactory ("net.sf.saxon" for open source editions (HE / B) 1218 // and "com.saxonica" for commercial editions (PE / EE / SA)) 1219 Class<?> factoryClass = factory.getClass(); 1220 if (factoryClass.getName().startsWith("net.sf.saxon") 1221 || factoryClass.getName().startsWith("com.saxonica")) { 1222 1223 // just in case there are multiple class loaders with different Saxon versions, use the 1224 // TransformerFactory's class loader to find Saxon support classes 1225 ClassLoader loader = factoryClass.getClassLoader(); 1226 1227 // try to find Saxon's MessageWarner class that redirects <xsl:message> to the ErrorListener 1228 Class<?> messageWarner = null; 1229 try { 1230 // Saxon >= 9.3 1231 messageWarner = loader.loadClass("net.sf.saxon.serialize.MessageWarner"); 1232 } catch (ClassNotFoundException cnfe) { 1233 try { 1234 // Saxon < 9.3 (including Saxon-B / -SA) 1235 messageWarner = loader.loadClass("net.sf.saxon.event.MessageWarner"); 1236 } catch (ClassNotFoundException cnfe2) { 1237 LOG.warn("Error loading Saxon's net.sf.saxon.serialize.MessageWarner class from the classpath!" 1238 + " <xsl:message> output will not be redirected to the ErrorListener!"); 1239 } 1240 } 1241 1242 if (messageWarner != null) { 1243 // set net.sf.saxon.FeatureKeys.MESSAGE_EMITTER_CLASS 1244 factory.setAttribute("http://saxon.sf.net/feature/messageEmitterClass", messageWarner.getName()); 1245 } 1246 } 1247 } 1248 1249 public SAXParserFactory createSAXParserFactory() { 1250 SAXParserFactory sfactory = SAXParserFactory.newInstance(); 1251 // Need to setup XMLReader security feature by default 1252 try { 1253 sfactory.setFeature(javax.xml.XMLConstants.FEATURE_SECURE_PROCESSING, true); 1254 } catch (Exception e) { 1255 LOG.warn("SAXParser doesn't support the feature {} with value {}, due to {}.", new Object[]{javax.xml.XMLConstants.FEATURE_SECURE_PROCESSING, "true", e}); 1256 } 1257 try { 1258 sfactory.setFeature("http://xml.org/sax/features/external-general-entities", false); 1259 } catch (Exception e) { 1260 LOG.warn("SAXParser doesn't support the feature {} with value {}, due to {}.", 1261 new Object[]{"http://xml.org/sax/features/external-general-entities", false, e}); 1262 } 1263 sfactory.setNamespaceAware(true); 1264 return sfactory; 1265 } 1266}