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