001/* 002 * Copyright 2002-2019 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.oxm.support; 018 019import java.io.IOException; 020import java.io.InputStream; 021import java.io.OutputStream; 022import java.io.Reader; 023import java.io.StringReader; 024import java.io.Writer; 025 026import javax.xml.parsers.DocumentBuilder; 027import javax.xml.parsers.DocumentBuilderFactory; 028import javax.xml.parsers.ParserConfigurationException; 029import javax.xml.stream.XMLEventReader; 030import javax.xml.stream.XMLEventWriter; 031import javax.xml.stream.XMLStreamReader; 032import javax.xml.stream.XMLStreamWriter; 033import javax.xml.transform.Result; 034import javax.xml.transform.Source; 035import javax.xml.transform.dom.DOMResult; 036import javax.xml.transform.dom.DOMSource; 037import javax.xml.transform.sax.SAXResult; 038import javax.xml.transform.sax.SAXSource; 039import javax.xml.transform.stax.StAXSource; 040import javax.xml.transform.stream.StreamResult; 041import javax.xml.transform.stream.StreamSource; 042 043import org.apache.commons.logging.Log; 044import org.apache.commons.logging.LogFactory; 045import org.w3c.dom.Document; 046import org.w3c.dom.Node; 047import org.xml.sax.ContentHandler; 048import org.xml.sax.EntityResolver; 049import org.xml.sax.InputSource; 050import org.xml.sax.SAXException; 051import org.xml.sax.XMLReader; 052import org.xml.sax.ext.LexicalHandler; 053 054import org.springframework.lang.Nullable; 055import org.springframework.oxm.Marshaller; 056import org.springframework.oxm.Unmarshaller; 057import org.springframework.oxm.UnmarshallingFailureException; 058import org.springframework.oxm.XmlMappingException; 059import org.springframework.util.Assert; 060import org.springframework.util.xml.StaxUtils; 061 062/** 063 * Abstract implementation of the {@code Marshaller} and {@code Unmarshaller} interface. 064 * This implementation inspects the given {@code Source} or {@code Result}, and 065 * delegates further handling to overridable template methods. 066 * 067 * @author Arjen Poutsma 068 * @author Juergen Hoeller 069 * @since 3.0 070 */ 071public abstract class AbstractMarshaller implements Marshaller, Unmarshaller { 072 073 private static final EntityResolver NO_OP_ENTITY_RESOLVER = 074 (publicId, systemId) -> new InputSource(new StringReader("")); 075 076 /** Logger available to subclasses. */ 077 protected final Log logger = LogFactory.getLog(getClass()); 078 079 private boolean supportDtd = false; 080 081 private boolean processExternalEntities = false; 082 083 @Nullable 084 private DocumentBuilderFactory documentBuilderFactory; 085 086 private final Object documentBuilderFactoryMonitor = new Object(); 087 088 089 /** 090 * Indicate whether DTD parsing should be supported. 091 * <p>Default is {@code false} meaning that DTD is disabled. 092 */ 093 public void setSupportDtd(boolean supportDtd) { 094 this.supportDtd = supportDtd; 095 } 096 097 /** 098 * Return whether DTD parsing is supported. 099 */ 100 public boolean isSupportDtd() { 101 return this.supportDtd; 102 } 103 104 /** 105 * Indicate whether external XML entities are processed when unmarshalling. 106 * <p>Default is {@code false}, meaning that external entities are not resolved. 107 * Note that processing of external entities will only be enabled/disabled when the 108 * {@code Source} passed to {@link #unmarshal(Source)} is a {@link SAXSource} or 109 * {@link StreamSource}. It has no effect for {@link DOMSource} or {@link StAXSource} 110 * instances. 111 * <p><strong>Note:</strong> setting this option to {@code true} also 112 * automatically sets {@link #setSupportDtd} to {@code true}. 113 */ 114 public void setProcessExternalEntities(boolean processExternalEntities) { 115 this.processExternalEntities = processExternalEntities; 116 if (processExternalEntities) { 117 this.supportDtd = true; 118 } 119 } 120 121 /** 122 * Return whether XML external entities are allowed. 123 * @see #createXmlReader() 124 */ 125 public boolean isProcessExternalEntities() { 126 return this.processExternalEntities; 127 } 128 129 130 /** 131 * Build a new {@link Document} from this marshaller's {@link DocumentBuilderFactory}, 132 * as a placeholder for a DOM node. 133 * @see #createDocumentBuilderFactory() 134 * @see #createDocumentBuilder(DocumentBuilderFactory) 135 */ 136 protected Document buildDocument() { 137 try { 138 DocumentBuilder documentBuilder; 139 synchronized (this.documentBuilderFactoryMonitor) { 140 if (this.documentBuilderFactory == null) { 141 this.documentBuilderFactory = createDocumentBuilderFactory(); 142 } 143 documentBuilder = createDocumentBuilder(this.documentBuilderFactory); 144 } 145 return documentBuilder.newDocument(); 146 } 147 catch (ParserConfigurationException ex) { 148 throw new UnmarshallingFailureException("Could not create document placeholder: " + ex.getMessage(), ex); 149 } 150 } 151 152 /** 153 * Create a {@code DocumentBuilder} that this marshaller will use for creating 154 * DOM documents when passed an empty {@code DOMSource}. 155 * <p>The resulting {@code DocumentBuilderFactory} is cached, so this method 156 * will only be called once. 157 * @return the DocumentBuilderFactory 158 * @throws ParserConfigurationException if thrown by JAXP methods 159 */ 160 protected DocumentBuilderFactory createDocumentBuilderFactory() throws ParserConfigurationException { 161 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 162 factory.setValidating(false); 163 factory.setNamespaceAware(true); 164 factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", !isSupportDtd()); 165 factory.setFeature("http://xml.org/sax/features/external-general-entities", isProcessExternalEntities()); 166 return factory; 167 } 168 169 /** 170 * Create a {@code DocumentBuilder} that this marshaller will use for creating 171 * DOM documents when passed an empty {@code DOMSource}. 172 * <p>Can be overridden in subclasses, adding further initialization of the builder. 173 * @param factory the {@code DocumentBuilderFactory} that the DocumentBuilder should be created with 174 * @return the {@code DocumentBuilder} 175 * @throws ParserConfigurationException if thrown by JAXP methods 176 */ 177 protected DocumentBuilder createDocumentBuilder(DocumentBuilderFactory factory) 178 throws ParserConfigurationException { 179 180 DocumentBuilder documentBuilder = factory.newDocumentBuilder(); 181 if (!isProcessExternalEntities()) { 182 documentBuilder.setEntityResolver(NO_OP_ENTITY_RESOLVER); 183 } 184 return documentBuilder; 185 } 186 187 /** 188 * Create an {@code XMLReader} that this marshaller will when passed an empty {@code SAXSource}. 189 * @return the XMLReader 190 * @throws SAXException if thrown by JAXP methods 191 */ 192 @SuppressWarnings("deprecation") // on JDK 9 193 protected XMLReader createXmlReader() throws SAXException { 194 XMLReader xmlReader = org.xml.sax.helpers.XMLReaderFactory.createXMLReader(); 195 xmlReader.setFeature("http://apache.org/xml/features/disallow-doctype-decl", !isSupportDtd()); 196 xmlReader.setFeature("http://xml.org/sax/features/external-general-entities", isProcessExternalEntities()); 197 if (!isProcessExternalEntities()) { 198 xmlReader.setEntityResolver(NO_OP_ENTITY_RESOLVER); 199 } 200 return xmlReader; 201 } 202 203 /** 204 * Determine the default encoding to use for marshalling or unmarshalling from 205 * a byte stream, or {@code null} if none. 206 * <p>The default implementation returns {@code null}. 207 */ 208 @Nullable 209 protected String getDefaultEncoding() { 210 return null; 211 } 212 213 214 // Marshalling 215 216 /** 217 * Marshals the object graph with the given root into the provided {@code javax.xml.transform.Result}. 218 * <p>This implementation inspects the given result, and calls {@code marshalDomResult}, 219 * {@code marshalSaxResult}, or {@code marshalStreamResult}. 220 * @param graph the root of the object graph to marshal 221 * @param result the result to marshal to 222 * @throws IOException if an I/O exception occurs 223 * @throws XmlMappingException if the given object cannot be marshalled to the result 224 * @throws IllegalArgumentException if {@code result} if neither a {@code DOMResult}, 225 * a {@code SAXResult}, nor a {@code StreamResult} 226 * @see #marshalDomResult(Object, javax.xml.transform.dom.DOMResult) 227 * @see #marshalSaxResult(Object, javax.xml.transform.sax.SAXResult) 228 * @see #marshalStreamResult(Object, javax.xml.transform.stream.StreamResult) 229 */ 230 @Override 231 public final void marshal(Object graph, Result result) throws IOException, XmlMappingException { 232 if (result instanceof DOMResult) { 233 marshalDomResult(graph, (DOMResult) result); 234 } 235 else if (StaxUtils.isStaxResult(result)) { 236 marshalStaxResult(graph, result); 237 } 238 else if (result instanceof SAXResult) { 239 marshalSaxResult(graph, (SAXResult) result); 240 } 241 else if (result instanceof StreamResult) { 242 marshalStreamResult(graph, (StreamResult) result); 243 } 244 else { 245 throw new IllegalArgumentException("Unknown Result type: " + result.getClass()); 246 } 247 } 248 249 /** 250 * Template method for handling {@code DOMResult}s. 251 * <p>This implementation delegates to {@code marshalDomNode}. 252 * @param graph the root of the object graph to marshal 253 * @param domResult the {@code DOMResult} 254 * @throws XmlMappingException if the given object cannot be marshalled to the result 255 * @throws IllegalArgumentException if the {@code domResult} is empty 256 * @see #marshalDomNode(Object, org.w3c.dom.Node) 257 */ 258 protected void marshalDomResult(Object graph, DOMResult domResult) throws XmlMappingException { 259 if (domResult.getNode() == null) { 260 domResult.setNode(buildDocument()); 261 } 262 marshalDomNode(graph, domResult.getNode()); 263 } 264 265 /** 266 * Template method for handling {@code StaxResult}s. 267 * <p>This implementation delegates to {@code marshalXMLSteamWriter} or 268 * {@code marshalXMLEventConsumer}, depending on what is contained in the 269 * {@code StaxResult}. 270 * @param graph the root of the object graph to marshal 271 * @param staxResult a JAXP 1.4 {@link StAXSource} 272 * @throws XmlMappingException if the given object cannot be marshalled to the result 273 * @throws IllegalArgumentException if the {@code domResult} is empty 274 * @see #marshalDomNode(Object, org.w3c.dom.Node) 275 */ 276 protected void marshalStaxResult(Object graph, Result staxResult) throws XmlMappingException { 277 XMLStreamWriter streamWriter = StaxUtils.getXMLStreamWriter(staxResult); 278 if (streamWriter != null) { 279 marshalXmlStreamWriter(graph, streamWriter); 280 } 281 else { 282 XMLEventWriter eventWriter = StaxUtils.getXMLEventWriter(staxResult); 283 if (eventWriter != null) { 284 marshalXmlEventWriter(graph, eventWriter); 285 } 286 else { 287 throw new IllegalArgumentException("StaxResult contains neither XMLStreamWriter nor XMLEventConsumer"); 288 } 289 } 290 } 291 292 /** 293 * Template method for handling {@code SAXResult}s. 294 * <p>This implementation delegates to {@code marshalSaxHandlers}. 295 * @param graph the root of the object graph to marshal 296 * @param saxResult the {@code SAXResult} 297 * @throws XmlMappingException if the given object cannot be marshalled to the result 298 * @see #marshalSaxHandlers(Object, org.xml.sax.ContentHandler, org.xml.sax.ext.LexicalHandler) 299 */ 300 protected void marshalSaxResult(Object graph, SAXResult saxResult) throws XmlMappingException { 301 ContentHandler contentHandler = saxResult.getHandler(); 302 Assert.notNull(contentHandler, "ContentHandler not set on SAXResult"); 303 LexicalHandler lexicalHandler = saxResult.getLexicalHandler(); 304 marshalSaxHandlers(graph, contentHandler, lexicalHandler); 305 } 306 307 /** 308 * Template method for handling {@code StreamResult}s. 309 * <p>This implementation delegates to {@code marshalOutputStream} or {@code marshalWriter}, 310 * depending on what is contained in the {@code StreamResult} 311 * @param graph the root of the object graph to marshal 312 * @param streamResult the {@code StreamResult} 313 * @throws IOException if an I/O Exception occurs 314 * @throws XmlMappingException if the given object cannot be marshalled to the result 315 * @throws IllegalArgumentException if {@code streamResult} does neither 316 * contain an {@code OutputStream} nor a {@code Writer} 317 */ 318 protected void marshalStreamResult(Object graph, StreamResult streamResult) 319 throws XmlMappingException, IOException { 320 321 if (streamResult.getOutputStream() != null) { 322 marshalOutputStream(graph, streamResult.getOutputStream()); 323 } 324 else if (streamResult.getWriter() != null) { 325 marshalWriter(graph, streamResult.getWriter()); 326 } 327 else { 328 throw new IllegalArgumentException("StreamResult contains neither OutputStream nor Writer"); 329 } 330 } 331 332 333 // Unmarshalling 334 335 /** 336 * Unmarshals the given provided {@code javax.xml.transform.Source} into an object graph. 337 * <p>This implementation inspects the given result, and calls {@code unmarshalDomSource}, 338 * {@code unmarshalSaxSource}, or {@code unmarshalStreamSource}. 339 * @param source the source to marshal from 340 * @return the object graph 341 * @throws IOException if an I/O Exception occurs 342 * @throws XmlMappingException if the given source cannot be mapped to an object 343 * @throws IllegalArgumentException if {@code source} is neither a {@code DOMSource}, 344 * a {@code SAXSource}, nor a {@code StreamSource} 345 * @see #unmarshalDomSource(javax.xml.transform.dom.DOMSource) 346 * @see #unmarshalSaxSource(javax.xml.transform.sax.SAXSource) 347 * @see #unmarshalStreamSource(javax.xml.transform.stream.StreamSource) 348 */ 349 @Override 350 public final Object unmarshal(Source source) throws IOException, XmlMappingException { 351 if (source instanceof DOMSource) { 352 return unmarshalDomSource((DOMSource) source); 353 } 354 else if (StaxUtils.isStaxSource(source)) { 355 return unmarshalStaxSource(source); 356 } 357 else if (source instanceof SAXSource) { 358 return unmarshalSaxSource((SAXSource) source); 359 } 360 else if (source instanceof StreamSource) { 361 return unmarshalStreamSource((StreamSource) source); 362 } 363 else { 364 throw new IllegalArgumentException("Unknown Source type: " + source.getClass()); 365 } 366 } 367 368 /** 369 * Template method for handling {@code DOMSource}s. 370 * <p>This implementation delegates to {@code unmarshalDomNode}. 371 * If the given source is empty, an empty source {@code Document} 372 * will be created as a placeholder. 373 * @param domSource the {@code DOMSource} 374 * @return the object graph 375 * @throws XmlMappingException if the given source cannot be mapped to an object 376 * @throws IllegalArgumentException if the {@code domSource} is empty 377 * @see #unmarshalDomNode(org.w3c.dom.Node) 378 */ 379 protected Object unmarshalDomSource(DOMSource domSource) throws XmlMappingException { 380 if (domSource.getNode() == null) { 381 domSource.setNode(buildDocument()); 382 } 383 try { 384 return unmarshalDomNode(domSource.getNode()); 385 } 386 catch (NullPointerException ex) { 387 if (!isSupportDtd()) { 388 throw new UnmarshallingFailureException("NPE while unmarshalling. " + 389 "This can happen on JDK 1.6 due to the presence of DTD " + 390 "declarations, which are disabled.", ex); 391 } 392 throw ex; 393 } 394 } 395 396 /** 397 * Template method for handling {@code StaxSource}s. 398 * <p>This implementation delegates to {@code unmarshalXmlStreamReader} or 399 * {@code unmarshalXmlEventReader}. 400 * @param staxSource the {@code StaxSource} 401 * @return the object graph 402 * @throws XmlMappingException if the given source cannot be mapped to an object 403 */ 404 protected Object unmarshalStaxSource(Source staxSource) throws XmlMappingException { 405 XMLStreamReader streamReader = StaxUtils.getXMLStreamReader(staxSource); 406 if (streamReader != null) { 407 return unmarshalXmlStreamReader(streamReader); 408 } 409 else { 410 XMLEventReader eventReader = StaxUtils.getXMLEventReader(staxSource); 411 if (eventReader != null) { 412 return unmarshalXmlEventReader(eventReader); 413 } 414 else { 415 throw new IllegalArgumentException("StaxSource contains neither XMLStreamReader nor XMLEventReader"); 416 } 417 } 418 } 419 420 /** 421 * Template method for handling {@code SAXSource}s. 422 * <p>This implementation delegates to {@code unmarshalSaxReader}. 423 * @param saxSource the {@code SAXSource} 424 * @return the object graph 425 * @throws XmlMappingException if the given source cannot be mapped to an object 426 * @throws IOException if an I/O Exception occurs 427 * @see #unmarshalSaxReader(org.xml.sax.XMLReader, org.xml.sax.InputSource) 428 */ 429 protected Object unmarshalSaxSource(SAXSource saxSource) throws XmlMappingException, IOException { 430 if (saxSource.getXMLReader() == null) { 431 try { 432 saxSource.setXMLReader(createXmlReader()); 433 } 434 catch (SAXException ex) { 435 throw new UnmarshallingFailureException("Could not create XMLReader for SAXSource", ex); 436 } 437 } 438 if (saxSource.getInputSource() == null) { 439 saxSource.setInputSource(new InputSource()); 440 } 441 try { 442 return unmarshalSaxReader(saxSource.getXMLReader(), saxSource.getInputSource()); 443 } 444 catch (NullPointerException ex) { 445 if (!isSupportDtd()) { 446 throw new UnmarshallingFailureException("NPE while unmarshalling. " + 447 "This can happen on JDK 1.6 due to the presence of DTD " + 448 "declarations, which are disabled."); 449 } 450 throw ex; 451 } 452 } 453 454 /** 455 * Template method for handling {@code StreamSource}s. 456 * <p>This implementation delegates to {@code unmarshalInputStream} or {@code unmarshalReader}. 457 * @param streamSource the {@code StreamSource} 458 * @return the object graph 459 * @throws IOException if an I/O exception occurs 460 * @throws XmlMappingException if the given source cannot be mapped to an object 461 */ 462 protected Object unmarshalStreamSource(StreamSource streamSource) throws XmlMappingException, IOException { 463 if (streamSource.getInputStream() != null) { 464 if (isProcessExternalEntities() && isSupportDtd()) { 465 return unmarshalInputStream(streamSource.getInputStream()); 466 } 467 else { 468 InputSource inputSource = new InputSource(streamSource.getInputStream()); 469 inputSource.setEncoding(getDefaultEncoding()); 470 return unmarshalSaxSource(new SAXSource(inputSource)); 471 } 472 } 473 else if (streamSource.getReader() != null) { 474 if (isProcessExternalEntities() && isSupportDtd()) { 475 return unmarshalReader(streamSource.getReader()); 476 } 477 else { 478 return unmarshalSaxSource(new SAXSource(new InputSource(streamSource.getReader()))); 479 } 480 } 481 else { 482 return unmarshalSaxSource(new SAXSource(new InputSource(streamSource.getSystemId()))); 483 } 484 } 485 486 487 // Abstract template methods 488 489 /** 490 * Abstract template method for marshalling the given object graph to a DOM {@code Node}. 491 * <p>In practice, node is be a {@code Document} node, a {@code DocumentFragment} node, 492 * or a {@code Element} node. In other words, a node that accepts children. 493 * @param graph the root of the object graph to marshal 494 * @param node the DOM node that will contain the result tree 495 * @throws XmlMappingException if the given object cannot be marshalled to the DOM node 496 * @see org.w3c.dom.Document 497 * @see org.w3c.dom.DocumentFragment 498 * @see org.w3c.dom.Element 499 */ 500 protected abstract void marshalDomNode(Object graph, Node node) 501 throws XmlMappingException; 502 503 /** 504 * Abstract template method for marshalling the given object to a StAX {@code XMLEventWriter}. 505 * @param graph the root of the object graph to marshal 506 * @param eventWriter the {@code XMLEventWriter} to write to 507 * @throws XmlMappingException if the given object cannot be marshalled to the DOM node 508 */ 509 protected abstract void marshalXmlEventWriter(Object graph, XMLEventWriter eventWriter) 510 throws XmlMappingException; 511 512 /** 513 * Abstract template method for marshalling the given object to a StAX {@code XMLStreamWriter}. 514 * @param graph the root of the object graph to marshal 515 * @param streamWriter the {@code XMLStreamWriter} to write to 516 * @throws XmlMappingException if the given object cannot be marshalled to the DOM node 517 */ 518 protected abstract void marshalXmlStreamWriter(Object graph, XMLStreamWriter streamWriter) 519 throws XmlMappingException; 520 521 /** 522 * Abstract template method for marshalling the given object graph to a SAX {@code ContentHandler}. 523 * @param graph the root of the object graph to marshal 524 * @param contentHandler the SAX {@code ContentHandler} 525 * @param lexicalHandler the SAX2 {@code LexicalHandler}. Can be {@code null}. 526 * @throws XmlMappingException if the given object cannot be marshalled to the handlers 527 */ 528 protected abstract void marshalSaxHandlers( 529 Object graph, ContentHandler contentHandler, @Nullable LexicalHandler lexicalHandler) 530 throws XmlMappingException; 531 532 /** 533 * Abstract template method for marshalling the given object graph to a {@code OutputStream}. 534 * @param graph the root of the object graph to marshal 535 * @param outputStream the {@code OutputStream} to write to 536 * @throws XmlMappingException if the given object cannot be marshalled to the writer 537 * @throws IOException if an I/O exception occurs 538 */ 539 protected abstract void marshalOutputStream(Object graph, OutputStream outputStream) 540 throws XmlMappingException, IOException; 541 542 /** 543 * Abstract template method for marshalling the given object graph to a {@code Writer}. 544 * @param graph the root of the object graph to marshal 545 * @param writer the {@code Writer} to write to 546 * @throws XmlMappingException if the given object cannot be marshalled to the writer 547 * @throws IOException if an I/O exception occurs 548 */ 549 protected abstract void marshalWriter(Object graph, Writer writer) 550 throws XmlMappingException, IOException; 551 552 /** 553 * Abstract template method for unmarshalling from a given DOM {@code Node}. 554 * @param node the DOM node that contains the objects to be unmarshalled 555 * @return the object graph 556 * @throws XmlMappingException if the given DOM node cannot be mapped to an object 557 */ 558 protected abstract Object unmarshalDomNode(Node node) throws XmlMappingException; 559 560 /** 561 * Abstract template method for unmarshalling from a given Stax {@code XMLEventReader}. 562 * @param eventReader the {@code XMLEventReader} to read from 563 * @return the object graph 564 * @throws XmlMappingException if the given event reader cannot be converted to an object 565 */ 566 protected abstract Object unmarshalXmlEventReader(XMLEventReader eventReader) 567 throws XmlMappingException; 568 569 /** 570 * Abstract template method for unmarshalling from a given Stax {@code XMLStreamReader}. 571 * @param streamReader the {@code XMLStreamReader} to read from 572 * @return the object graph 573 * @throws XmlMappingException if the given stream reader cannot be converted to an object 574 */ 575 protected abstract Object unmarshalXmlStreamReader(XMLStreamReader streamReader) 576 throws XmlMappingException; 577 578 /** 579 * Abstract template method for unmarshalling using a given SAX {@code XMLReader} 580 * and {@code InputSource}. 581 * @param xmlReader the SAX {@code XMLReader} to parse with 582 * @param inputSource the input source to parse from 583 * @return the object graph 584 * @throws XmlMappingException if the given reader and input source cannot be converted to an object 585 * @throws IOException if an I/O exception occurs 586 */ 587 protected abstract Object unmarshalSaxReader(XMLReader xmlReader, InputSource inputSource) 588 throws XmlMappingException, IOException; 589 590 /** 591 * Abstract template method for unmarshalling from a given {@code InputStream}. 592 * @param inputStream the {@code InputStreamStream} to read from 593 * @return the object graph 594 * @throws XmlMappingException if the given stream cannot be converted to an object 595 * @throws IOException if an I/O exception occurs 596 */ 597 protected abstract Object unmarshalInputStream(InputStream inputStream) 598 throws XmlMappingException, IOException; 599 600 /** 601 * Abstract template method for unmarshalling from a given {@code Reader}. 602 * @param reader the {@code Reader} to read from 603 * @return the object graph 604 * @throws XmlMappingException if the given reader cannot be converted to an object 605 * @throws IOException if an I/O exception occurs 606 */ 607 protected abstract Object unmarshalReader(Reader reader) 608 throws XmlMappingException, IOException; 609 610}