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