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