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