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