1 /* 2 * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.xml.internal.bind.v2.runtime.unmarshaller; 27 28 import java.io.IOException; 29 import java.io.InputStream; 30 31 import javax.xml.bind.JAXBContext; 32 import javax.xml.bind.JAXBElement; 33 import javax.xml.bind.JAXBException; 34 import javax.xml.bind.PropertyException; 35 import javax.xml.bind.UnmarshalException; 36 import javax.xml.bind.Unmarshaller; 37 import javax.xml.bind.UnmarshallerHandler; 38 import javax.xml.bind.ValidationEvent; 39 import javax.xml.bind.ValidationEventHandler; 40 import javax.xml.bind.annotation.adapters.XmlAdapter; 41 import javax.xml.bind.attachment.AttachmentUnmarshaller; 42 import javax.xml.bind.helpers.AbstractUnmarshallerImpl; 43 import javax.xml.stream.XMLEventReader; 44 import javax.xml.stream.XMLStreamConstants; 45 import javax.xml.stream.XMLStreamException; 46 import javax.xml.stream.XMLStreamReader; 47 import javax.xml.stream.events.XMLEvent; 48 import javax.xml.transform.Source; 49 import javax.xml.transform.dom.DOMSource; 50 import javax.xml.transform.sax.SAXSource; 51 import javax.xml.transform.stream.StreamSource; 52 import javax.xml.validation.Schema; 53 54 import com.sun.xml.internal.bind.IDResolver; 55 import com.sun.xml.internal.bind.api.ClassResolver; 56 import com.sun.xml.internal.bind.unmarshaller.DOMScanner; 57 import com.sun.xml.internal.bind.unmarshaller.InfosetScanner; 58 import com.sun.xml.internal.bind.unmarshaller.Messages; 59 import com.sun.xml.internal.bind.v2.ClassFactory; 60 import com.sun.xml.internal.bind.v2.runtime.AssociationMap; 61 import com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl; 62 import com.sun.xml.internal.bind.v2.runtime.JaxBeanInfo; 63 import com.sun.xml.internal.bind.v2.util.XmlFactory; 64 65 import java.io.Closeable; 66 import javax.xml.parsers.ParserConfigurationException; 67 import javax.xml.parsers.SAXParserFactory; 68 import org.w3c.dom.Document; 69 import org.w3c.dom.Element; 70 import org.w3c.dom.Node; 71 import org.xml.sax.InputSource; 72 import org.xml.sax.SAXException; 73 import org.xml.sax.XMLReader; 74 import org.xml.sax.helpers.DefaultHandler; 75 76 /** 77 * Default Unmarshaller implementation. 78 * 79 * <p> 80 * This class can be extended by the generated code to provide 81 * type-safe unmarshall methods. 82 * 83 * @author 84 * <a href="mailto:kohsuke.kawaguchi@sun.com">Kohsuke KAWAGUCHI</a> 85 */ 86 public final class UnmarshallerImpl extends AbstractUnmarshallerImpl implements ValidationEventHandler, Closeable 87 { 88 /** Owning {@link JAXBContext} */ 89 protected final JAXBContextImpl context; 90 91 /** 92 * schema which will be used to validate during calls to unmarshal 93 */ 94 private Schema schema; 95 96 public final UnmarshallingContext coordinator; 97 98 /** Unmarshaller.Listener */ 99 private Listener externalListener; 100 101 /** 102 * The attachment unmarshaller used to support MTOM and swaRef. 103 */ 104 private AttachmentUnmarshaller attachmentUnmarshaller; 105 private IDResolver idResolver = new DefaultIDResolver(); 106 UnmarshallerImpl( JAXBContextImpl context, AssociationMap assoc )107 public UnmarshallerImpl( JAXBContextImpl context, AssociationMap assoc ) { 108 this.context = context; 109 this.coordinator = new UnmarshallingContext( this, assoc ); 110 111 try { 112 setEventHandler(this); 113 } catch (JAXBException e) { 114 throw new AssertionError(e); // impossible 115 } 116 } 117 getUnmarshallerHandler()118 public UnmarshallerHandler getUnmarshallerHandler() { 119 return getUnmarshallerHandler(true,null); 120 } 121 122 private XMLReader reader = null; 123 124 /** 125 * Obtains a configured XMLReader. 126 * 127 * This method is used when the client-specified 128 * {@link SAXSource} object doesn't have XMLReader. 129 * 130 * {@link Unmarshaller} is not re-entrant, so we will 131 * only use one instance of XMLReader. 132 * 133 * Overriden in order to fix potential security issue. 134 */ 135 @Override getXMLReader()136 protected XMLReader getXMLReader() throws JAXBException { 137 if (reader == null) { 138 try { 139 SAXParserFactory parserFactory = XmlFactory.createParserFactory(context.disableSecurityProcessing); 140 // there is no point in asking a validation because 141 // there is no guarantee that the document will come with 142 // a proper schemaLocation. 143 parserFactory.setValidating(false); 144 reader = parserFactory.newSAXParser().getXMLReader(); 145 } catch (ParserConfigurationException e) { 146 throw new JAXBException(e); 147 } catch (SAXException e) { 148 throw new JAXBException(e); 149 } 150 } 151 return reader; 152 } 153 getUnmarshallerHandler( boolean intern, JaxBeanInfo expectedType )154 private SAXConnector getUnmarshallerHandler( boolean intern, JaxBeanInfo expectedType ) { 155 XmlVisitor h = createUnmarshallerHandler(null, false, expectedType); 156 if (intern) { 157 h = new InterningXmlVisitor(h); 158 } 159 return new SAXConnector(h,null); 160 } 161 162 /** 163 * Creates and configures a new unmarshalling pipe line. 164 * Depending on the setting, we put a validator as a filter. 165 * 166 * @return 167 * A component that implements both {@link UnmarshallerHandler} 168 * and {@link ValidationEventHandler}. All the parsing errors 169 * should be reported to this error handler for the unmarshalling 170 * process to work correctly. 171 * 172 * Also, returned handler expects all the XML names to be interned. 173 * 174 */ createUnmarshallerHandler(InfosetScanner scanner, boolean inplace, JaxBeanInfo expectedType )175 public final XmlVisitor createUnmarshallerHandler(InfosetScanner scanner, boolean inplace, JaxBeanInfo expectedType ) { 176 177 coordinator.reset(scanner,inplace,expectedType,idResolver); 178 XmlVisitor unmarshaller = coordinator; 179 180 // delegate to JAXP 1.3 for validation if the client provided a schema 181 if (schema != null) { 182 unmarshaller = new ValidatingUnmarshaller(schema,unmarshaller); 183 } 184 185 if(attachmentUnmarshaller!=null && attachmentUnmarshaller.isXOPPackage()) { 186 unmarshaller = new MTOMDecorator(this,unmarshaller,attachmentUnmarshaller); 187 } 188 189 return unmarshaller; 190 } 191 192 private static final DefaultHandler dummyHandler = new DefaultHandler(); 193 needsInterning( XMLReader reader )194 public static boolean needsInterning( XMLReader reader ) { 195 // attempt to set it to true, which could fail 196 try { 197 reader.setFeature("http://xml.org/sax/features/string-interning",true); 198 } catch (SAXException e) { 199 // if it fails that's fine. we'll work around on our side 200 } 201 202 try { 203 if (reader.getFeature("http://xml.org/sax/features/string-interning")) { 204 return false; // no need for intern 205 } 206 } catch (SAXException e) { 207 // unrecognized/unsupported 208 } 209 // otherwise we need intern 210 return true; 211 } 212 unmarshal( XMLReader reader, InputSource source )213 protected Object unmarshal( XMLReader reader, InputSource source ) throws JAXBException { 214 return unmarshal0(reader,source,null); 215 } 216 unmarshal( XMLReader reader, InputSource source, Class<T> expectedType )217 protected <T> JAXBElement<T> unmarshal( XMLReader reader, InputSource source, Class<T> expectedType ) throws JAXBException { 218 if(expectedType==null) { 219 throw new IllegalArgumentException(); 220 } 221 return (JAXBElement)unmarshal0(reader,source,getBeanInfo(expectedType)); 222 } 223 unmarshal0( XMLReader reader, InputSource source, JaxBeanInfo expectedType )224 private Object unmarshal0( XMLReader reader, InputSource source, JaxBeanInfo expectedType ) throws JAXBException { 225 226 SAXConnector connector = getUnmarshallerHandler(needsInterning(reader),expectedType); 227 228 reader.setContentHandler(connector); 229 // saxErrorHandler will be set by the getUnmarshallerHandler method. 230 // configure XMLReader so that the error will be sent to it. 231 // This is essential for the UnmarshallerHandler to be able to abort 232 // unmarshalling when an error is found. 233 // 234 // Note that when this XMLReader is provided by the client code, 235 // it might be already configured to call a client error handler. 236 // This will clobber such handler, if any. 237 // 238 // Ryan noted that we might want to report errors to such a client 239 // error handler as well. 240 reader.setErrorHandler(coordinator); 241 242 try { 243 reader.parse(source); 244 } catch( IOException e ) { 245 coordinator.clearStates(); 246 throw new UnmarshalException(e); 247 } catch( SAXException e ) { 248 coordinator.clearStates(); 249 throw createUnmarshalException(e); 250 } 251 252 Object result = connector.getResult(); 253 254 // avoid keeping unnecessary references too long to let the GC 255 // reclaim more memory. 256 // setting null upsets some parsers, so use a dummy instance instead. 257 reader.setContentHandler(dummyHandler); 258 reader.setErrorHandler(dummyHandler); 259 260 return result; 261 } 262 263 @Override unmarshal( Source source, Class<T> expectedType )264 public <T> JAXBElement<T> unmarshal( Source source, Class<T> expectedType ) throws JAXBException { 265 if (source instanceof SAXSource) { 266 SAXSource ss = (SAXSource) source; 267 268 XMLReader locReader = ss.getXMLReader(); 269 if (locReader == null) { 270 locReader = getXMLReader(); 271 } 272 273 return unmarshal(locReader, ss.getInputSource(), expectedType); 274 } 275 if (source instanceof StreamSource) { 276 return unmarshal(getXMLReader(), streamSourceToInputSource((StreamSource) source), expectedType); 277 } 278 if (source instanceof DOMSource) { 279 return unmarshal(((DOMSource) source).getNode(), expectedType); 280 } 281 282 // we don't handle other types of Source 283 throw new IllegalArgumentException(); 284 } 285 unmarshal0( Source source, JaxBeanInfo expectedType )286 public Object unmarshal0( Source source, JaxBeanInfo expectedType ) throws JAXBException { 287 if (source instanceof SAXSource) { 288 SAXSource ss = (SAXSource) source; 289 290 XMLReader locReader = ss.getXMLReader(); 291 if (locReader == null) { 292 locReader = getXMLReader(); 293 } 294 295 return unmarshal0(locReader, ss.getInputSource(), expectedType); 296 } 297 if (source instanceof StreamSource) { 298 return unmarshal0(getXMLReader(), streamSourceToInputSource((StreamSource) source), expectedType); 299 } 300 if (source instanceof DOMSource) { 301 return unmarshal0(((DOMSource) source).getNode(), expectedType); 302 } 303 304 // we don't handle other types of Source 305 throw new IllegalArgumentException(); 306 } 307 308 309 @Override getEventHandler()310 public final ValidationEventHandler getEventHandler() { 311 try { 312 return super.getEventHandler(); 313 } catch (JAXBException e) { 314 // impossible 315 throw new AssertionError(); 316 } 317 } 318 319 /** 320 * Returns true if an event handler is installed. 321 * <p> 322 * The default handler ignores any errors, and for that this method returns false. 323 */ hasEventHandler()324 public final boolean hasEventHandler() { 325 return getEventHandler()!=this; 326 } 327 328 @Override unmarshal(Node node, Class<T> expectedType)329 public <T> JAXBElement<T> unmarshal(Node node, Class<T> expectedType) throws JAXBException { 330 if (expectedType == null) { 331 throw new IllegalArgumentException(); 332 } 333 return (JAXBElement)unmarshal0(node,getBeanInfo(expectedType)); 334 } 335 unmarshal( Node node )336 public final Object unmarshal( Node node ) throws JAXBException { 337 return unmarshal0(node,null); 338 } 339 340 // just to make the the test harness happy by making this method accessible 341 @Deprecated unmarshal( SAXSource source )342 public final Object unmarshal( SAXSource source ) throws JAXBException { 343 return super.unmarshal(source); 344 } 345 unmarshal0( Node node, JaxBeanInfo expectedType )346 public final Object unmarshal0( Node node, JaxBeanInfo expectedType ) throws JAXBException { 347 try { 348 final DOMScanner scanner = new DOMScanner(); 349 350 InterningXmlVisitor handler = new InterningXmlVisitor(createUnmarshallerHandler(null,false,expectedType)); 351 scanner.setContentHandler(new SAXConnector(handler,scanner)); 352 353 if(node.getNodeType() == Node.ELEMENT_NODE) { 354 scanner.scan((Element)node); 355 } else if(node.getNodeType() == Node.DOCUMENT_NODE) { 356 scanner.scan((Document)node); 357 } else { 358 throw new IllegalArgumentException("Unexpected node type: "+node); 359 } 360 361 Object retVal = handler.getContext().getResult(); 362 handler.getContext().clearResult(); 363 return retVal; 364 } catch( SAXException e ) { 365 throw createUnmarshalException(e); 366 } 367 } 368 369 @Override unmarshal(XMLStreamReader reader)370 public Object unmarshal(XMLStreamReader reader) throws JAXBException { 371 return unmarshal0(reader,null); 372 } 373 374 @Override unmarshal(XMLStreamReader reader, Class<T> expectedType)375 public <T> JAXBElement<T> unmarshal(XMLStreamReader reader, Class<T> expectedType) throws JAXBException { 376 if (expectedType==null) { 377 throw new IllegalArgumentException(); 378 } 379 return (JAXBElement)unmarshal0(reader,getBeanInfo(expectedType)); 380 } 381 unmarshal0(XMLStreamReader reader, JaxBeanInfo expectedType)382 public Object unmarshal0(XMLStreamReader reader, JaxBeanInfo expectedType) throws JAXBException { 383 if (reader == null) { 384 throw new IllegalArgumentException( 385 Messages.format(Messages.NULL_READER)); 386 } 387 388 int eventType = reader.getEventType(); 389 if (eventType != XMLStreamConstants.START_ELEMENT 390 && eventType != XMLStreamConstants.START_DOCUMENT) { 391 // TODO: convert eventType into event name 392 throw new IllegalStateException( 393 Messages.format(Messages.ILLEGAL_READER_STATE,eventType)); 394 } 395 396 XmlVisitor h = createUnmarshallerHandler(null,false,expectedType); 397 StAXConnector connector=StAXStreamConnector.create(reader,h); 398 399 try { 400 connector.bridge(); 401 } catch (XMLStreamException e) { 402 throw handleStreamException(e); 403 } 404 405 Object retVal = h.getContext().getResult(); 406 h.getContext().clearResult(); 407 return retVal; 408 } 409 410 @Override unmarshal(XMLEventReader reader, Class<T> expectedType)411 public <T> JAXBElement<T> unmarshal(XMLEventReader reader, Class<T> expectedType) throws JAXBException { 412 if(expectedType==null) { 413 throw new IllegalArgumentException(); 414 } 415 return (JAXBElement)unmarshal0(reader,getBeanInfo(expectedType)); 416 } 417 418 @Override unmarshal(XMLEventReader reader)419 public Object unmarshal(XMLEventReader reader) throws JAXBException { 420 return unmarshal0(reader,null); 421 } 422 unmarshal0(XMLEventReader reader,JaxBeanInfo expectedType)423 private Object unmarshal0(XMLEventReader reader,JaxBeanInfo expectedType) throws JAXBException { 424 if (reader == null) { 425 throw new IllegalArgumentException( 426 Messages.format(Messages.NULL_READER)); 427 } 428 429 try { 430 XMLEvent event = reader.peek(); 431 432 if (!event.isStartElement() && !event.isStartDocument()) { 433 // TODO: convert event into event name 434 throw new IllegalStateException( 435 Messages.format( 436 Messages.ILLEGAL_READER_STATE,event.getEventType())); 437 } 438 439 // Quick hack until SJSXP fixes 6270116 440 boolean isZephyr = reader.getClass().getName().equals("com.sun.xml.internal.stream.XMLReaderImpl"); 441 XmlVisitor h = createUnmarshallerHandler(null,false,expectedType); 442 if(!isZephyr) { 443 h = new InterningXmlVisitor(h); 444 } 445 new StAXEventConnector(reader,h).bridge(); 446 return h.getContext().getResult(); 447 } catch (XMLStreamException e) { 448 throw handleStreamException(e); 449 } 450 } 451 unmarshal0( InputStream input, JaxBeanInfo expectedType )452 public Object unmarshal0( InputStream input, JaxBeanInfo expectedType ) throws JAXBException { 453 return unmarshal0(getXMLReader(),new InputSource(input),expectedType); 454 } 455 handleStreamException(XMLStreamException e)456 private static JAXBException handleStreamException(XMLStreamException e) { 457 // StAXStreamConnector wraps SAXException to XMLStreamException. 458 // XMLStreamException doesn't print its nested stack trace when it prints 459 // its stack trace, so if we wrap XMLStreamException in JAXBException, 460 // it becomes harder to find out the real problem. 461 // So we unwrap them here. But we don't want to unwrap too eagerly, because 462 // that could throw away some meaningful exception information. 463 Throwable ne = e.getNestedException(); 464 if(ne instanceof JAXBException) { 465 return (JAXBException)ne; 466 } 467 if(ne instanceof SAXException) { 468 return new UnmarshalException(ne); 469 } 470 return new UnmarshalException(e); 471 } 472 473 @Override getProperty(String name)474 public Object getProperty(String name) throws PropertyException { 475 if(name.equals(IDResolver.class.getName())) { 476 return idResolver; 477 } 478 return super.getProperty(name); 479 } 480 481 @Override setProperty(String name, Object value)482 public void setProperty(String name, Object value) throws PropertyException { 483 if(name.equals(FACTORY)) { 484 coordinator.setFactories(value); 485 return; 486 } 487 if(name.equals(IDResolver.class.getName())) { 488 idResolver = (IDResolver)value; 489 return; 490 } 491 if(name.equals(ClassResolver.class.getName())) { 492 coordinator.classResolver = (ClassResolver)value; 493 return; 494 } 495 if(name.equals(ClassLoader.class.getName())) { 496 coordinator.classLoader = (ClassLoader)value; 497 return; 498 } 499 super.setProperty(name, value); 500 } 501 502 public static final String FACTORY = "com.sun.xml.internal.bind.ObjectFactory"; 503 504 @Override setSchema(Schema schema)505 public void setSchema(Schema schema) { 506 this.schema = schema; 507 } 508 509 @Override getSchema()510 public Schema getSchema() { 511 return schema; 512 } 513 514 @Override getAttachmentUnmarshaller()515 public AttachmentUnmarshaller getAttachmentUnmarshaller() { 516 return attachmentUnmarshaller; 517 } 518 519 @Override setAttachmentUnmarshaller(AttachmentUnmarshaller au)520 public void setAttachmentUnmarshaller(AttachmentUnmarshaller au) { 521 this.attachmentUnmarshaller = au; 522 } 523 524 /** 525 * @deprecated since 2.0 526 */ 527 @Override isValidating()528 public boolean isValidating() { 529 throw new UnsupportedOperationException(); 530 } 531 532 /** 533 * @deprecated since 2.0 534 */ 535 @Override setValidating(boolean validating)536 public void setValidating(boolean validating) { 537 throw new UnsupportedOperationException(); 538 } 539 540 @Override setAdapter(Class<A> type, A adapter)541 public <A extends XmlAdapter> void setAdapter(Class<A> type, A adapter) { 542 if (type==null) { 543 throw new IllegalArgumentException(); 544 } 545 coordinator.putAdapter(type,adapter); 546 } 547 548 @Override getAdapter(Class<A> type)549 public <A extends XmlAdapter> A getAdapter(Class<A> type) { 550 if(type==null) { 551 throw new IllegalArgumentException(); 552 } 553 if(coordinator.containsAdapter(type)) { 554 return coordinator.getAdapter(type); 555 } else { 556 return null; 557 } 558 } 559 560 // opening up for public use 561 @Override createUnmarshalException( SAXException e )562 public UnmarshalException createUnmarshalException( SAXException e ) { 563 return super.createUnmarshalException(e); 564 } 565 566 567 /** 568 * Default error handling behavior for {@link Unmarshaller}. 569 */ handleEvent(ValidationEvent event)570 public boolean handleEvent(ValidationEvent event) { 571 return event.getSeverity()!=ValidationEvent.FATAL_ERROR; 572 } 573 streamSourceToInputSource( StreamSource ss )574 private static InputSource streamSourceToInputSource( StreamSource ss ) { 575 InputSource is = new InputSource(); 576 is.setSystemId( ss.getSystemId() ); 577 is.setByteStream( ss.getInputStream() ); 578 is.setCharacterStream( ss.getReader() ); 579 580 return is; 581 } 582 getBeanInfo(Class<T> clazz)583 public <T> JaxBeanInfo<T> getBeanInfo(Class<T> clazz) throws JAXBException { 584 return context.getBeanInfo(clazz,true); 585 } 586 587 @Override getListener()588 public Listener getListener() { 589 return externalListener; 590 } 591 592 @Override setListener(Listener listener)593 public void setListener(Listener listener) { 594 externalListener = listener; 595 } 596 getContext()597 public UnmarshallingContext getContext() { 598 return coordinator; 599 } 600 601 @Override 602 @SuppressWarnings("FinalizeDeclaration") finalize()603 protected void finalize() throws Throwable { 604 try { 605 ClassFactory.cleanCache(); 606 } finally { 607 super.finalize(); 608 } 609 } 610 611 /** 612 * Must be called from same thread which created the UnmarshallerImpl instance. 613 * @throws IOException 614 */ close()615 public void close() throws IOException { 616 ClassFactory.cleanCache(); 617 } 618 619 } 620