1/*
2 * Copyright (c) 1997, 2013, 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
26package com.sun.xml.internal.bind.v2.runtime.unmarshaller;
27
28import java.io.IOException;
29import java.io.InputStream;
30
31import javax.xml.bind.JAXBContext;
32import javax.xml.bind.JAXBElement;
33import javax.xml.bind.JAXBException;
34import javax.xml.bind.PropertyException;
35import javax.xml.bind.UnmarshalException;
36import javax.xml.bind.Unmarshaller;
37import javax.xml.bind.UnmarshallerHandler;
38import javax.xml.bind.ValidationEvent;
39import javax.xml.bind.ValidationEventHandler;
40import javax.xml.bind.annotation.adapters.XmlAdapter;
41import javax.xml.bind.attachment.AttachmentUnmarshaller;
42import javax.xml.bind.helpers.AbstractUnmarshallerImpl;
43import javax.xml.stream.XMLEventReader;
44import javax.xml.stream.XMLStreamConstants;
45import javax.xml.stream.XMLStreamException;
46import javax.xml.stream.XMLStreamReader;
47import javax.xml.stream.events.XMLEvent;
48import javax.xml.transform.Source;
49import javax.xml.transform.dom.DOMSource;
50import javax.xml.transform.sax.SAXSource;
51import javax.xml.transform.stream.StreamSource;
52import javax.xml.validation.Schema;
53
54import com.sun.xml.internal.bind.IDResolver;
55import com.sun.xml.internal.bind.api.ClassResolver;
56import com.sun.xml.internal.bind.unmarshaller.DOMScanner;
57import com.sun.xml.internal.bind.unmarshaller.InfosetScanner;
58import com.sun.xml.internal.bind.unmarshaller.Messages;
59import com.sun.xml.internal.bind.v2.ClassFactory;
60import com.sun.xml.internal.bind.v2.runtime.AssociationMap;
61import com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl;
62import com.sun.xml.internal.bind.v2.runtime.JaxBeanInfo;
63import com.sun.xml.internal.bind.v2.util.XmlFactory;
64
65import java.io.Closeable;
66import javax.xml.parsers.ParserConfigurationException;
67import javax.xml.parsers.SAXParserFactory;
68import org.w3c.dom.Document;
69import org.w3c.dom.Element;
70import org.w3c.dom.Node;
71import org.xml.sax.InputSource;
72import org.xml.sax.SAXException;
73import org.xml.sax.XMLReader;
74import 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
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
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
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
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     */
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
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
213    protected Object unmarshal( XMLReader reader, InputSource source ) throws JAXBException {
214        return unmarshal0(reader,source,null);
215    }
216
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
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
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
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
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     */
324    public final boolean hasEventHandler() {
325        return getEventHandler()!=this;
326    }
327
328    @Override
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
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
342    public final Object unmarshal( SAXSource source ) throws JAXBException {
343        return super.unmarshal(source);
344    }
345
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
370    public Object unmarshal(XMLStreamReader reader) throws JAXBException {
371        return unmarshal0(reader,null);
372    }
373
374    @Override
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
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
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
419    public Object unmarshal(XMLEventReader reader) throws JAXBException {
420        return unmarshal0(reader,null);
421    }
422
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
452    public Object unmarshal0( InputStream input, JaxBeanInfo expectedType ) throws JAXBException {
453        return unmarshal0(getXMLReader(),new InputSource(input),expectedType);
454    }
455
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
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
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
505    public void setSchema(Schema schema) {
506        this.schema = schema;
507    }
508
509    @Override
510    public Schema getSchema() {
511        return schema;
512    }
513
514    @Override
515    public AttachmentUnmarshaller getAttachmentUnmarshaller() {
516        return attachmentUnmarshaller;
517    }
518
519    @Override
520    public void setAttachmentUnmarshaller(AttachmentUnmarshaller au) {
521        this.attachmentUnmarshaller = au;
522    }
523
524    /**
525     * @deprecated since 2.0
526     */
527    @Override
528    public boolean isValidating() {
529        throw new UnsupportedOperationException();
530    }
531
532    /**
533     * @deprecated since 2.0
534     */
535    @Override
536    public void setValidating(boolean validating) {
537        throw new UnsupportedOperationException();
538    }
539
540    @Override
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
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
562    public UnmarshalException createUnmarshalException( SAXException e ) {
563        return super.createUnmarshalException(e);
564    }
565
566
567    /**
568     * Default error handling behavior for {@link Unmarshaller}.
569     */
570    public boolean handleEvent(ValidationEvent event) {
571        return event.getSeverity()!=ValidationEvent.FATAL_ERROR;
572    }
573
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
583    public <T> JaxBeanInfo<T> getBeanInfo(Class<T> clazz) throws JAXBException {
584        return context.getBeanInfo(clazz,true);
585    }
586
587    @Override
588    public Listener getListener() {
589        return externalListener;
590    }
591
592    @Override
593    public void setListener(Listener listener) {
594        externalListener = listener;
595    }
596
597    public UnmarshallingContext getContext() {
598        return coordinator;
599    }
600
601    @Override
602    @SuppressWarnings("FinalizeDeclaration")
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     */
615    public void close() throws IOException {
616        ClassFactory.cleanCache();
617    }
618
619}
620