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