View Javadoc
1   /*
2    * Copyright (c) 2006, 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  
26  package javax.xml.bind;
27  
28  import javax.xml.bind.annotation.XmlRootElement;
29  import javax.xml.namespace.QName;
30  import javax.xml.transform.Result;
31  import javax.xml.transform.Source;
32  import javax.xml.transform.stream.StreamResult;
33  import javax.xml.transform.stream.StreamSource;
34  import java.beans.Introspector;
35  import java.io.File;
36  import java.io.IOException;
37  import java.io.InputStream;
38  import java.io.OutputStream;
39  import java.io.Reader;
40  import java.io.Writer;
41  import java.lang.ref.WeakReference;
42  import java.net.HttpURLConnection;
43  import java.net.URI;
44  import java.net.URISyntaxException;
45  import java.net.URL;
46  import java.net.URLConnection;
47  
48  /**
49   * Class that defines convenience methods for common, simple use of JAXB.
50   *
51   * <p>
52   * Methods defined in this class are convenience methods that combine several basic operations
53   * in the {@link JAXBContext}, {@link Unmarshaller}, and {@link Marshaller}.
54   *
55   * They are designed
56   * to be the prefered methods for developers new to JAXB. They have
57   * the following characterstics:
58   *
59   * <ol>
60   *  <li>Generally speaking, the performance is not necessarily optimal.
61   *      It is expected that people who need to write performance
62   *      critical code will use the rest of the JAXB API directly.
63   *  <li>Errors that happen during the processing is wrapped into
64   *      {@link DataBindingException} (which will have {@link JAXBException}
65   *      as its {@link Throwable#getCause() cause}. It is expected that
66   *      people who prefer the checked exception would use
67   *      the rest of the JAXB API directly.
68   * </ol>
69   *
70   * <p>
71   * In addition, the <tt>unmarshal</tt> methods have the following characteristic:
72   *
73   * <ol>
74   *  <li>Schema validation is not performed on the input XML.
75   *      The processing will try to continue even if there
76   *      are errors in the XML, as much as possible. Only as
77   *      the last resort, this method fails with {@link DataBindingException}.
78   * </ol>
79   *
80   * <p>
81   * Similarly, the <tt>marshal</tt> methods have the following characteristic:
82   * <ol>
83   *  <li>The processing will try to continue even if the Java object tree
84   *      does not meet the validity requirement. Only as
85   *      the last resort, this method fails with {@link DataBindingException}.
86   * </ol>
87   *
88   *
89   * <p>
90   * All the methods on this class require non-null arguments to all parameters.
91   * The <tt>unmarshal</tt> methods either fail with an exception or return
92   * a non-null value.
93   *
94   * @author Kohsuke Kawaguchi
95   * @since 2.1
96   */
97  public final class JAXB {
98      /**
99       * No instanciation is allowed.
100      */
101     private JAXB() {}
102 
103     /**
104      * To improve the performance, we'll cache the last {@link JAXBContext} used.
105      */
106     private static final class Cache {
107         final Class type;
108         final JAXBContext context;
109 
110         public Cache(Class type) throws JAXBException {
111             this.type = type;
112             this.context = JAXBContext.newInstance(type);
113         }
114     }
115 
116     /**
117      * Cache. We don't want to prevent the {@link Cache#type} from GC-ed,
118      * hence {@link WeakReference}.
119      */
120     private static volatile WeakReference<Cache> cache;
121 
122     /**
123      * Obtains the {@link JAXBContext} from the given type,
124      * by using the cache if possible.
125      *
126      * <p>
127      * We don't use locks to control access to {@link #cache}, but this code
128      * should be thread-safe thanks to the immutable {@link Cache} and {@code volatile}.
129      */
130     private static <T> JAXBContext getContext(Class<T> type) throws JAXBException {
131         WeakReference<Cache> c = cache;
132         if(c!=null) {
133             Cache d = c.get();
134             if(d!=null && d.type==type)
135                 return d.context;
136         }
137 
138         // overwrite the cache
139         Cache d = new Cache(type);
140         cache = new WeakReference<Cache>(d);
141 
142         return d.context;
143     }
144 
145     /**
146      * Reads in a Java object tree from the given XML input.
147      *
148      * @param xml
149      *      Reads the entire file as XML.
150      */
151     public static <T> T unmarshal( File xml, Class<T> type ) {
152         try {
153             JAXBElement<T> item = getContext(type).createUnmarshaller().unmarshal(new StreamSource(xml), type);
154             return item.getValue();
155         } catch (JAXBException e) {
156             throw new DataBindingException(e);
157         }
158     }
159 
160     /**
161      * Reads in a Java object tree from the given XML input.
162      *
163      * @param xml
164      *      The resource pointed by the URL is read in its entirety.
165      */
166     public static <T> T unmarshal( URL xml, Class<T> type ) {
167         try {
168             JAXBElement<T> item = getContext(type).createUnmarshaller().unmarshal(toSource(xml), type);
169             return item.getValue();
170         } catch (JAXBException e) {
171             throw new DataBindingException(e);
172         } catch (IOException e) {
173             throw new DataBindingException(e);
174         }
175     }
176 
177     /**
178      * Reads in a Java object tree from the given XML input.
179      *
180      * @param xml
181      *      The URI is {@link URI#toURL() turned into URL} and then
182      *      follows the handling of <tt>URL</tt>.
183      */
184     public static <T> T unmarshal( URI xml, Class<T> type ) {
185         try {
186             JAXBElement<T> item = getContext(type).createUnmarshaller().unmarshal(toSource(xml), type);
187             return item.getValue();
188         } catch (JAXBException e) {
189             throw new DataBindingException(e);
190         } catch (IOException e) {
191             throw new DataBindingException(e);
192         }
193     }
194 
195     /**
196      * Reads in a Java object tree from the given XML input.
197      *
198      * @param xml
199      *      The string is first interpreted as an absolute <tt>URI</tt>.
200      *      If it's not {@link URI#isAbsolute() a valid absolute URI},
201      *      then it's interpreted as a <tt>File</tt>
202      */
203     public static <T> T unmarshal( String xml, Class<T> type ) {
204         try {
205             JAXBElement<T> item = getContext(type).createUnmarshaller().unmarshal(toSource(xml), type);
206             return item.getValue();
207         } catch (JAXBException e) {
208             throw new DataBindingException(e);
209         } catch (IOException e) {
210             throw new DataBindingException(e);
211         }
212     }
213 
214     /**
215      * Reads in a Java object tree from the given XML input.
216      *
217      * @param xml
218      *      The entire stream is read as an XML infoset.
219      *      Upon a successful completion, the stream will be closed by this method.
220      */
221     public static <T> T unmarshal( InputStream xml, Class<T> type ) {
222         try {
223             JAXBElement<T> item = getContext(type).createUnmarshaller().unmarshal(toSource(xml), type);
224             return item.getValue();
225         } catch (JAXBException e) {
226             throw new DataBindingException(e);
227         } catch (IOException e) {
228             throw new DataBindingException(e);
229         }
230     }
231 
232     /**
233      * Reads in a Java object tree from the given XML input.
234      *
235      * @param xml
236      *      The character stream is read as an XML infoset.
237      *      The encoding declaration in the XML will be ignored.
238      *      Upon a successful completion, the stream will be closed by this method.
239      */
240     public static <T> T unmarshal( Reader xml, Class<T> type ) {
241         try {
242             JAXBElement<T> item = getContext(type).createUnmarshaller().unmarshal(toSource(xml), type);
243             return item.getValue();
244         } catch (JAXBException e) {
245             throw new DataBindingException(e);
246         } catch (IOException e) {
247             throw new DataBindingException(e);
248         }
249     }
250 
251     /**
252      * Reads in a Java object tree from the given XML input.
253      *
254      * @param xml
255      *      The XML infoset that the {@link Source} represents is read.
256      */
257     public static <T> T unmarshal( Source xml, Class<T> type ) {
258         try {
259             JAXBElement<T> item = getContext(type).createUnmarshaller().unmarshal(toSource(xml), type);
260             return item.getValue();
261         } catch (JAXBException e) {
262             throw new DataBindingException(e);
263         } catch (IOException e) {
264             throw new DataBindingException(e);
265         }
266     }
267 
268 
269 
270     /**
271      * Creates {@link Source} from various XML representation.
272      * See {@link #unmarshal} for the conversion rules.
273      */
274     private static Source toSource(Object xml) throws IOException {
275         if(xml==null)
276             throw new IllegalArgumentException("no XML is given");
277 
278         if (xml instanceof String) {
279             try {
280                 xml=new URI((String)xml);
281             } catch (URISyntaxException e) {
282                 xml=new File((String)xml);
283             }
284         }
285         if (xml instanceof File) {
286             File file = (File) xml;
287             return new StreamSource(file);
288         }
289         if (xml instanceof URI) {
290             URI uri = (URI) xml;
291             xml=uri.toURL();
292         }
293         if (xml instanceof URL) {
294             URL url = (URL) xml;
295             return new StreamSource(url.toExternalForm());
296         }
297         if (xml instanceof InputStream) {
298             InputStream in = (InputStream) xml;
299             return new StreamSource(in);
300         }
301         if (xml instanceof Reader) {
302             Reader r = (Reader) xml;
303             return new StreamSource(r);
304         }
305         if (xml instanceof Source) {
306             return (Source) xml;
307         }
308         throw new IllegalArgumentException("I don't understand how to handle "+xml.getClass());
309     }
310 
311     /**
312      * Writes a Java object tree to XML and store it to the specified location.
313      *
314      * @param jaxbObject
315      *      The Java object to be marshalled into XML. If this object is
316      *      a {@link JAXBElement}, it will provide the root tag name and
317      *      the body. If this object has {@link XmlRootElement}
318      *      on its class definition, that will be used as the root tag name
319      *      and the given object will provide the body. Otherwise,
320      *      the root tag name is {@link Introspector#decapitalize(String) infered} from
321      *      {@link Class#getSimpleName() the short class name}.
322      *      This parameter must not be null.
323      *
324      * @param xml
325      *      XML will be written to this file. If it already exists,
326      *      it will be overwritten.
327      *
328      * @throws DataBindingException
329      *      If the operation fails, such as due to I/O error, unbindable classes.
330      */
331     public static void marshal( Object jaxbObject, File xml ) {
332         _marshal(jaxbObject,xml);
333     }
334 
335     /**
336      * Writes a Java object tree to XML and store it to the specified location.
337      *
338      * @param jaxbObject
339      *      The Java object to be marshalled into XML. If this object is
340      *      a {@link JAXBElement}, it will provide the root tag name and
341      *      the body. If this object has {@link XmlRootElement}
342      *      on its class definition, that will be used as the root tag name
343      *      and the given object will provide the body. Otherwise,
344      *      the root tag name is {@link Introspector#decapitalize(String) infered} from
345      *      {@link Class#getSimpleName() the short class name}.
346      *      This parameter must not be null.
347      *
348      * @param xml
349      *      The XML will be {@link URLConnection#getOutputStream() sent} to the
350      *      resource pointed by this URL. Note that not all <tt>URL</tt>s support
351      *      such operation, and exact semantics depends on the <tt>URL</tt>
352      *      implementations. In case of {@link HttpURLConnection HTTP URLs},
353      *      this will perform HTTP POST.
354      *
355      * @throws DataBindingException
356      *      If the operation fails, such as due to I/O error, unbindable classes.
357      */
358     public static void marshal( Object jaxbObject, URL xml ) {
359         _marshal(jaxbObject,xml);
360     }
361 
362     /**
363      * Writes a Java object tree to XML and store it to the specified location.
364      *
365      * @param jaxbObject
366      *      The Java object to be marshalled into XML. If this object is
367      *      a {@link JAXBElement}, it will provide the root tag name and
368      *      the body. If this object has {@link XmlRootElement}
369      *      on its class definition, that will be used as the root tag name
370      *      and the given object will provide the body. Otherwise,
371      *      the root tag name is {@link Introspector#decapitalize(String) infered} from
372      *      {@link Class#getSimpleName() the short class name}.
373      *      This parameter must not be null.
374      *
375      * @param xml
376      *      The URI is {@link URI#toURL() turned into URL} and then
377      *      follows the handling of <tt>URL</tt>. See above.
378      *
379      * @throws DataBindingException
380      *      If the operation fails, such as due to I/O error, unbindable classes.
381      */
382     public static void marshal( Object jaxbObject, URI xml ) {
383         _marshal(jaxbObject,xml);
384     }
385 
386     /**
387      * Writes a Java object tree to XML and store it to the specified location.
388      *
389      * @param jaxbObject
390      *      The Java object to be marshalled into XML. If this object is
391      *      a {@link JAXBElement}, it will provide the root tag name and
392      *      the body. If this object has {@link XmlRootElement}
393      *      on its class definition, that will be used as the root tag name
394      *      and the given object will provide the body. Otherwise,
395      *      the root tag name is {@link Introspector#decapitalize(String) infered} from
396      *      {@link Class#getSimpleName() the short class name}.
397      *      This parameter must not be null.
398      *
399      * @param xml
400      *      The string is first interpreted as an absolute <tt>URI</tt>.
401      *      If it's not {@link URI#isAbsolute() a valid absolute URI},
402      *      then it's interpreted as a <tt>File</tt>
403      *
404      * @throws DataBindingException
405      *      If the operation fails, such as due to I/O error, unbindable classes.
406      */
407     public static void marshal( Object jaxbObject, String xml ) {
408         _marshal(jaxbObject,xml);
409     }
410 
411     /**
412      * Writes a Java object tree to XML and store it to the specified location.
413      *
414      * @param jaxbObject
415      *      The Java object to be marshalled into XML. If this object is
416      *      a {@link JAXBElement}, it will provide the root tag name and
417      *      the body. If this object has {@link XmlRootElement}
418      *      on its class definition, that will be used as the root tag name
419      *      and the given object will provide the body. Otherwise,
420      *      the root tag name is {@link Introspector#decapitalize(String) infered} from
421      *      {@link Class#getSimpleName() the short class name}.
422      *      This parameter must not be null.
423      *
424      * @param xml
425      *      The XML will be sent to the given {@link OutputStream}.
426      *      Upon a successful completion, the stream will be closed by this method.
427      *
428      * @throws DataBindingException
429      *      If the operation fails, such as due to I/O error, unbindable classes.
430      */
431     public static void marshal( Object jaxbObject, OutputStream xml ) {
432         _marshal(jaxbObject,xml);
433     }
434 
435     /**
436      * Writes a Java object tree to XML and store it to the specified location.
437      *
438      * @param jaxbObject
439      *      The Java object to be marshalled into XML. If this object is
440      *      a {@link JAXBElement}, it will provide the root tag name and
441      *      the body. If this object has {@link XmlRootElement}
442      *      on its class definition, that will be used as the root tag name
443      *      and the given object will provide the body. Otherwise,
444      *      the root tag name is {@link Introspector#decapitalize(String) infered} from
445      *      {@link Class#getSimpleName() the short class name}.
446      *      This parameter must not be null.
447      *
448      * @param xml
449      *      The XML will be sent as a character stream to the given {@link Writer}.
450      *      Upon a successful completion, the stream will be closed by this method.
451      *
452      * @throws DataBindingException
453      *      If the operation fails, such as due to I/O error, unbindable classes.
454      */
455     public static void marshal( Object jaxbObject, Writer xml ) {
456         _marshal(jaxbObject,xml);
457     }
458 
459     /**
460      * Writes a Java object tree to XML and store it to the specified location.
461      *
462      * @param jaxbObject
463      *      The Java object to be marshalled into XML. If this object is
464      *      a {@link JAXBElement}, it will provide the root tag name and
465      *      the body. If this object has {@link XmlRootElement}
466      *      on its class definition, that will be used as the root tag name
467      *      and the given object will provide the body. Otherwise,
468      *      the root tag name is {@link Introspector#decapitalize(String) infered} from
469      *      {@link Class#getSimpleName() the short class name}.
470      *      This parameter must not be null.
471      *
472      * @param xml
473      *      The XML will be sent to the {@link Result} object.
474      *
475      * @throws DataBindingException
476      *      If the operation fails, such as due to I/O error, unbindable classes.
477      */
478     public static void marshal( Object jaxbObject, Result xml ) {
479         _marshal(jaxbObject,xml);
480     }
481 
482     /**
483      * Writes a Java object tree to XML and store it to the specified location.
484      *
485      * <p>
486      * This method is a convenience method that combines several basic operations
487      * in the {@link JAXBContext} and {@link Marshaller}. This method is designed
488      * to be the prefered method for developers new to JAXB. This method
489      * has the following characterstics:
490      *
491      * <ol>
492      *  <li>Generally speaking, the performance is not necessarily optimal.
493      *      It is expected that those people who need to write performance
494      *      critical code will use the rest of the JAXB API directly.
495      *  <li>Errors that happen during the processing is wrapped into
496      *      {@link DataBindingException} (which will have {@link JAXBException}
497      *      as its {@link Throwable#getCause() cause}. It is expected that
498      *      those people who prefer the checked exception would use
499      *      the rest of the JAXB API directly.
500      * </ol>
501      *
502      * @param jaxbObject
503      *      The Java object to be marshalled into XML. If this object is
504      *      a {@link JAXBElement}, it will provide the root tag name and
505      *      the body. If this object has {@link XmlRootElement}
506      *      on its class definition, that will be used as the root tag name
507      *      and the given object will provide the body. Otherwise,
508      *      the root tag name is {@link Introspector#decapitalize(String) infered} from
509      *      {@link Class#getSimpleName() the short class name}.
510      *      This parameter must not be null.
511      *
512      * @param xml
513      *      Represents the receiver of XML. Objects of the following types are allowed.
514      *
515      *      <table><tr>
516      *          <th>Type</th>
517      *          <th>Operation</th>
518      *      </tr><tr>
519      *          <td>{@link File}</td>
520      *          <td>XML will be written to this file. If it already exists,
521      *              it will be overwritten.</td>
522      *      </tr><tr>
523      *          <td>{@link URL}</td>
524      *          <td>The XML will be {@link URLConnection#getOutputStream() sent} to the
525      *              resource pointed by this URL. Note that not all <tt>URL</tt>s support
526      *              such operation, and exact semantics depends on the <tt>URL</tt>
527      *              implementations. In case of {@link HttpURLConnection HTTP URLs},
528      *              this will perform HTTP POST.</td>
529      *      </tr><tr>
530      *          <td>{@link URI}</td>
531      *          <td>The URI is {@link URI#toURL() turned into URL} and then
532      *              follows the handling of <tt>URL</tt>. See above.</td>
533      *      </tr><tr>
534      *          <td>{@link String}</td>
535      *          <td>The string is first interpreted as an absolute <tt>URI</tt>.
536      *              If it's not {@link URI#isAbsolute() a valid absolute URI},
537      *              then it's interpreted as a <tt>File</tt></td>
538      *      </tr><tr>
539      *          <td>{@link OutputStream}</td>
540      *          <td>The XML will be sent to the given {@link OutputStream}.
541      *              Upon a successful completion, the stream will be closed by this method.</td>
542      *      </tr><tr>
543      *          <td>{@link Writer}</td>
544      *          <td>The XML will be sent as a character stream to the given {@link Writer}.
545      *              Upon a successful completion, the stream will be closed by this method.</td>
546      *      </tr><tr>
547      *          <td>{@link Result}</td>
548      *          <td>The XML will be sent to the {@link Result} object.</td>
549      *      </tr></table>
550      *
551      * @throws DataBindingException
552      *      If the operation fails, such as due to I/O error, unbindable classes.
553      */
554     private static void _marshal( Object jaxbObject, Object xml ) {
555         try {
556             JAXBContext context;
557 
558             if(jaxbObject instanceof JAXBElement) {
559                 context = getContext(((JAXBElement<?>)jaxbObject).getDeclaredType());
560             } else {
561                 Class<?> clazz = jaxbObject.getClass();
562                 XmlRootElement r = clazz.getAnnotation(XmlRootElement.class);
563                 context = getContext(clazz);
564                 if(r==null) {
565                     // we need to infer the name
566                     jaxbObject = new JAXBElement(new QName(inferName(clazz)),clazz,jaxbObject);
567                 }
568             }
569 
570             Marshaller m = context.createMarshaller();
571             m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT,true);
572             m.marshal(jaxbObject, toResult(xml));
573         } catch (JAXBException e) {
574             throw new DataBindingException(e);
575         } catch (IOException e) {
576             throw new DataBindingException(e);
577         }
578     }
579 
580     private static String inferName(Class clazz) {
581         return Introspector.decapitalize(clazz.getSimpleName());
582     }
583 
584     /**
585      * Creates {@link Result} from various XML representation.
586      * See {@link #_marshal(Object,Object)} for the conversion rules.
587      */
588     private static Result toResult(Object xml) throws IOException {
589         if(xml==null)
590             throw new IllegalArgumentException("no XML is given");
591 
592         if (xml instanceof String) {
593             try {
594                 xml=new URI((String)xml);
595             } catch (URISyntaxException e) {
596                 xml=new File((String)xml);
597             }
598         }
599         if (xml instanceof File) {
600             File file = (File) xml;
601             return new StreamResult(file);
602         }
603         if (xml instanceof URI) {
604             URI uri = (URI) xml;
605             xml=uri.toURL();
606         }
607         if (xml instanceof URL) {
608             URL url = (URL) xml;
609             URLConnection con = url.openConnection();
610             con.setDoOutput(true);
611             con.setDoInput(false);
612             con.connect();
613             return new StreamResult(con.getOutputStream());
614         }
615         if (xml instanceof OutputStream) {
616             OutputStream os = (OutputStream) xml;
617             return new StreamResult(os);
618         }
619         if (xml instanceof Writer) {
620             Writer w = (Writer)xml;
621             return new StreamResult(w);
622         }
623         if (xml instanceof Result) {
624             return (Result) xml;
625         }
626         throw new IllegalArgumentException("I don't understand how to handle "+xml.getClass());
627     }
628 
629 }