/* * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.xml.internal.bind.v2.runtime.unmarshaller; import java.io.IOException; import java.io.InputStream; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBElement; import javax.xml.bind.JAXBException; import javax.xml.bind.PropertyException; import javax.xml.bind.UnmarshalException; import javax.xml.bind.Unmarshaller; import javax.xml.bind.UnmarshallerHandler; import javax.xml.bind.ValidationEvent; import javax.xml.bind.ValidationEventHandler; import javax.xml.bind.annotation.adapters.XmlAdapter; import javax.xml.bind.attachment.AttachmentUnmarshaller; import javax.xml.bind.helpers.AbstractUnmarshallerImpl; import javax.xml.stream.XMLEventReader; import javax.xml.stream.XMLStreamConstants; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamReader; import javax.xml.stream.events.XMLEvent; import javax.xml.transform.Source; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.sax.SAXSource; import javax.xml.transform.stream.StreamSource; import javax.xml.validation.Schema; import com.sun.xml.internal.bind.IDResolver; import com.sun.xml.internal.bind.api.ClassResolver; import com.sun.xml.internal.bind.unmarshaller.DOMScanner; import com.sun.xml.internal.bind.unmarshaller.InfosetScanner; import com.sun.xml.internal.bind.unmarshaller.Messages; import com.sun.xml.internal.bind.v2.ClassFactory; import com.sun.xml.internal.bind.v2.runtime.AssociationMap; import com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl; import com.sun.xml.internal.bind.v2.runtime.JaxBeanInfo; import com.sun.xml.internal.bind.v2.util.XmlFactory; import java.io.Closeable; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParserFactory; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.XMLReader; import org.xml.sax.helpers.DefaultHandler; /** * Default Unmarshaller implementation. * *

* This class can be extended by the generated code to provide * type-safe unmarshall methods. * * @author * Kohsuke KAWAGUCHI */ public final class UnmarshallerImpl extends AbstractUnmarshallerImpl implements ValidationEventHandler, Closeable { /** Owning {@link JAXBContext} */ protected final JAXBContextImpl context; /** * schema which will be used to validate during calls to unmarshal */ private Schema schema; public final UnmarshallingContext coordinator; /** Unmarshaller.Listener */ private Listener externalListener; /** * The attachment unmarshaller used to support MTOM and swaRef. */ private AttachmentUnmarshaller attachmentUnmarshaller; private IDResolver idResolver = new DefaultIDResolver(); public UnmarshallerImpl( JAXBContextImpl context, AssociationMap assoc ) { this.context = context; this.coordinator = new UnmarshallingContext( this, assoc ); try { setEventHandler(this); } catch (JAXBException e) { throw new AssertionError(e); // impossible } } public UnmarshallerHandler getUnmarshallerHandler() { return getUnmarshallerHandler(true,null); } private XMLReader reader = null; /** * Obtains a configured XMLReader. * * This method is used when the client-specified * {@link SAXSource} object doesn't have XMLReader. * * {@link Unmarshaller} is not re-entrant, so we will * only use one instance of XMLReader. * * Overriden in order to fix potential security issue. */ @Override protected XMLReader getXMLReader() throws JAXBException { if (reader == null) { try { SAXParserFactory parserFactory = XmlFactory.createParserFactory(context.disableSecurityProcessing); // there is no point in asking a validation because // there is no guarantee that the document will come with // a proper schemaLocation. parserFactory.setValidating(false); reader = parserFactory.newSAXParser().getXMLReader(); } catch (ParserConfigurationException e) { throw new JAXBException(e); } catch (SAXException e) { throw new JAXBException(e); } } return reader; } private SAXConnector getUnmarshallerHandler( boolean intern, JaxBeanInfo expectedType ) { XmlVisitor h = createUnmarshallerHandler(null, false, expectedType); if (intern) { h = new InterningXmlVisitor(h); } return new SAXConnector(h,null); } /** * Creates and configures a new unmarshalling pipe line. * Depending on the setting, we put a validator as a filter. * * @return * A component that implements both {@link UnmarshallerHandler} * and {@link ValidationEventHandler}. All the parsing errors * should be reported to this error handler for the unmarshalling * process to work correctly. * * Also, returned handler expects all the XML names to be interned. * */ public final XmlVisitor createUnmarshallerHandler(InfosetScanner scanner, boolean inplace, JaxBeanInfo expectedType ) { coordinator.reset(scanner,inplace,expectedType,idResolver); XmlVisitor unmarshaller = coordinator; // delegate to JAXP 1.3 for validation if the client provided a schema if (schema != null) { unmarshaller = new ValidatingUnmarshaller(schema,unmarshaller); } if(attachmentUnmarshaller!=null && attachmentUnmarshaller.isXOPPackage()) { unmarshaller = new MTOMDecorator(this,unmarshaller,attachmentUnmarshaller); } return unmarshaller; } private static final DefaultHandler dummyHandler = new DefaultHandler(); public static boolean needsInterning( XMLReader reader ) { // attempt to set it to true, which could fail try { reader.setFeature("http://xml.org/sax/features/string-interning",true); } catch (SAXException e) { // if it fails that's fine. we'll work around on our side } try { if (reader.getFeature("http://xml.org/sax/features/string-interning")) { return false; // no need for intern } } catch (SAXException e) { // unrecognized/unsupported } // otherwise we need intern return true; } protected Object unmarshal( XMLReader reader, InputSource source ) throws JAXBException { return unmarshal0(reader,source,null); } protected JAXBElement unmarshal( XMLReader reader, InputSource source, Class expectedType ) throws JAXBException { if(expectedType==null) { throw new IllegalArgumentException(); } return (JAXBElement)unmarshal0(reader,source,getBeanInfo(expectedType)); } private Object unmarshal0( XMLReader reader, InputSource source, JaxBeanInfo expectedType ) throws JAXBException { SAXConnector connector = getUnmarshallerHandler(needsInterning(reader),expectedType); reader.setContentHandler(connector); // saxErrorHandler will be set by the getUnmarshallerHandler method. // configure XMLReader so that the error will be sent to it. // This is essential for the UnmarshallerHandler to be able to abort // unmarshalling when an error is found. // // Note that when this XMLReader is provided by the client code, // it might be already configured to call a client error handler. // This will clobber such handler, if any. // // Ryan noted that we might want to report errors to such a client // error handler as well. reader.setErrorHandler(coordinator); try { reader.parse(source); } catch( IOException e ) { coordinator.clearStates(); throw new UnmarshalException(e); } catch( SAXException e ) { coordinator.clearStates(); throw createUnmarshalException(e); } Object result = connector.getResult(); // avoid keeping unnecessary references too long to let the GC // reclaim more memory. // setting null upsets some parsers, so use a dummy instance instead. reader.setContentHandler(dummyHandler); reader.setErrorHandler(dummyHandler); return result; } @Override public JAXBElement unmarshal( Source source, Class expectedType ) throws JAXBException { if (source instanceof SAXSource) { SAXSource ss = (SAXSource) source; XMLReader locReader = ss.getXMLReader(); if (locReader == null) { locReader = getXMLReader(); } return unmarshal(locReader, ss.getInputSource(), expectedType); } if (source instanceof StreamSource) { return unmarshal(getXMLReader(), streamSourceToInputSource((StreamSource) source), expectedType); } if (source instanceof DOMSource) { return unmarshal(((DOMSource) source).getNode(), expectedType); } // we don't handle other types of Source throw new IllegalArgumentException(); } public Object unmarshal0( Source source, JaxBeanInfo expectedType ) throws JAXBException { if (source instanceof SAXSource) { SAXSource ss = (SAXSource) source; XMLReader locReader = ss.getXMLReader(); if (locReader == null) { locReader = getXMLReader(); } return unmarshal0(locReader, ss.getInputSource(), expectedType); } if (source instanceof StreamSource) { return unmarshal0(getXMLReader(), streamSourceToInputSource((StreamSource) source), expectedType); } if (source instanceof DOMSource) { return unmarshal0(((DOMSource) source).getNode(), expectedType); } // we don't handle other types of Source throw new IllegalArgumentException(); } @Override public final ValidationEventHandler getEventHandler() { try { return super.getEventHandler(); } catch (JAXBException e) { // impossible throw new AssertionError(); } } /** * Returns true if an event handler is installed. *

* The default handler ignores any errors, and for that this method returns false. */ public final boolean hasEventHandler() { return getEventHandler()!=this; } @Override public JAXBElement unmarshal(Node node, Class expectedType) throws JAXBException { if (expectedType == null) { throw new IllegalArgumentException(); } return (JAXBElement)unmarshal0(node,getBeanInfo(expectedType)); } public final Object unmarshal( Node node ) throws JAXBException { return unmarshal0(node,null); } // just to make the the test harness happy by making this method accessible @Deprecated public final Object unmarshal( SAXSource source ) throws JAXBException { return super.unmarshal(source); } public final Object unmarshal0( Node node, JaxBeanInfo expectedType ) throws JAXBException { try { final DOMScanner scanner = new DOMScanner(); InterningXmlVisitor handler = new InterningXmlVisitor(createUnmarshallerHandler(null,false,expectedType)); scanner.setContentHandler(new SAXConnector(handler,scanner)); if(node.getNodeType() == Node.ELEMENT_NODE) { scanner.scan((Element)node); } else if(node.getNodeType() == Node.DOCUMENT_NODE) { scanner.scan((Document)node); } else { throw new IllegalArgumentException("Unexpected node type: "+node); } Object retVal = handler.getContext().getResult(); handler.getContext().clearResult(); return retVal; } catch( SAXException e ) { throw createUnmarshalException(e); } } @Override public Object unmarshal(XMLStreamReader reader) throws JAXBException { return unmarshal0(reader,null); } @Override public JAXBElement unmarshal(XMLStreamReader reader, Class expectedType) throws JAXBException { if (expectedType==null) { throw new IllegalArgumentException(); } return (JAXBElement)unmarshal0(reader,getBeanInfo(expectedType)); } public Object unmarshal0(XMLStreamReader reader, JaxBeanInfo expectedType) throws JAXBException { if (reader == null) { throw new IllegalArgumentException( Messages.format(Messages.NULL_READER)); } int eventType = reader.getEventType(); if (eventType != XMLStreamConstants.START_ELEMENT && eventType != XMLStreamConstants.START_DOCUMENT) { // TODO: convert eventType into event name throw new IllegalStateException( Messages.format(Messages.ILLEGAL_READER_STATE,eventType)); } XmlVisitor h = createUnmarshallerHandler(null,false,expectedType); StAXConnector connector=StAXStreamConnector.create(reader,h); try { connector.bridge(); } catch (XMLStreamException e) { throw handleStreamException(e); } Object retVal = h.getContext().getResult(); h.getContext().clearResult(); return retVal; } @Override public JAXBElement unmarshal(XMLEventReader reader, Class expectedType) throws JAXBException { if(expectedType==null) { throw new IllegalArgumentException(); } return (JAXBElement)unmarshal0(reader,getBeanInfo(expectedType)); } @Override public Object unmarshal(XMLEventReader reader) throws JAXBException { return unmarshal0(reader,null); } private Object unmarshal0(XMLEventReader reader,JaxBeanInfo expectedType) throws JAXBException { if (reader == null) { throw new IllegalArgumentException( Messages.format(Messages.NULL_READER)); } try { XMLEvent event = reader.peek(); if (!event.isStartElement() && !event.isStartDocument()) { // TODO: convert event into event name throw new IllegalStateException( Messages.format( Messages.ILLEGAL_READER_STATE,event.getEventType())); } // Quick hack until SJSXP fixes 6270116 boolean isZephyr = reader.getClass().getName().equals("com.sun.xml.internal.stream.XMLReaderImpl"); XmlVisitor h = createUnmarshallerHandler(null,false,expectedType); if(!isZephyr) { h = new InterningXmlVisitor(h); } new StAXEventConnector(reader,h).bridge(); return h.getContext().getResult(); } catch (XMLStreamException e) { throw handleStreamException(e); } } public Object unmarshal0( InputStream input, JaxBeanInfo expectedType ) throws JAXBException { return unmarshal0(getXMLReader(),new InputSource(input),expectedType); } private static JAXBException handleStreamException(XMLStreamException e) { // StAXStreamConnector wraps SAXException to XMLStreamException. // XMLStreamException doesn't print its nested stack trace when it prints // its stack trace, so if we wrap XMLStreamException in JAXBException, // it becomes harder to find out the real problem. // So we unwrap them here. But we don't want to unwrap too eagerly, because // that could throw away some meaningful exception information. Throwable ne = e.getNestedException(); if(ne instanceof JAXBException) { return (JAXBException)ne; } if(ne instanceof SAXException) { return new UnmarshalException(ne); } return new UnmarshalException(e); } @Override public Object getProperty(String name) throws PropertyException { if(name.equals(IDResolver.class.getName())) { return idResolver; } return super.getProperty(name); } @Override public void setProperty(String name, Object value) throws PropertyException { if(name.equals(FACTORY)) { coordinator.setFactories(value); return; } if(name.equals(IDResolver.class.getName())) { idResolver = (IDResolver)value; return; } if(name.equals(ClassResolver.class.getName())) { coordinator.classResolver = (ClassResolver)value; return; } if(name.equals(ClassLoader.class.getName())) { coordinator.classLoader = (ClassLoader)value; return; } super.setProperty(name, value); } public static final String FACTORY = "com.sun.xml.internal.bind.ObjectFactory"; @Override public void setSchema(Schema schema) { this.schema = schema; } @Override public Schema getSchema() { return schema; } @Override public AttachmentUnmarshaller getAttachmentUnmarshaller() { return attachmentUnmarshaller; } @Override public void setAttachmentUnmarshaller(AttachmentUnmarshaller au) { this.attachmentUnmarshaller = au; } /** * @deprecated since 2.0 */ @Override public boolean isValidating() { throw new UnsupportedOperationException(); } /** * @deprecated since 2.0 */ @Override public void setValidating(boolean validating) { throw new UnsupportedOperationException(); } @Override public void setAdapter(Class type, A adapter) { if (type==null) { throw new IllegalArgumentException(); } coordinator.putAdapter(type,adapter); } @Override public A getAdapter(Class type) { if(type==null) { throw new IllegalArgumentException(); } if(coordinator.containsAdapter(type)) { return coordinator.getAdapter(type); } else { return null; } } // opening up for public use @Override public UnmarshalException createUnmarshalException( SAXException e ) { return super.createUnmarshalException(e); } /** * Default error handling behavior for {@link Unmarshaller}. */ public boolean handleEvent(ValidationEvent event) { return event.getSeverity()!=ValidationEvent.FATAL_ERROR; } private static InputSource streamSourceToInputSource( StreamSource ss ) { InputSource is = new InputSource(); is.setSystemId( ss.getSystemId() ); is.setByteStream( ss.getInputStream() ); is.setCharacterStream( ss.getReader() ); return is; } public JaxBeanInfo getBeanInfo(Class clazz) throws JAXBException { return context.getBeanInfo(clazz,true); } @Override public Listener getListener() { return externalListener; } @Override public void setListener(Listener listener) { externalListener = listener; } public UnmarshallingContext getContext() { return coordinator; } @Override @SuppressWarnings("FinalizeDeclaration") protected void finalize() throws Throwable { try { ClassFactory.cleanCache(); } finally { super.finalize(); } } /** * Must be called from same thread which created the UnmarshallerImpl instance. * @throws IOException */ public void close() throws IOException { ClassFactory.cleanCache(); } }