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