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