101101/**
102102 * Implementation of the <code>Marshaller</code> interface for JAXB 2.0.
103103 *
104- * <p>The typical usage will be to set either the <code> contextPath</code> or the <code> classesToBeBound</code> property
105- * on this bean, possibly customize the marshaller and unmarshaller by setting properties, schemas, adapters, and
106- * listeners, and to refer to it.
104+ * <p>The typical usage will be to set either the " contextPath" or the " classesToBeBound"
105+ * property on this bean, possibly customize the marshaller and unmarshaller by setting
106+ * properties, schemas, adapters, and listeners, and to refer to it.
107107 *
108108 * @author Arjen Poutsma
109+ * @author Juergen Hoeller
109110 * @since 3.0
110111 * @see #setContextPath(String)
111112 * @see #setClassesToBeBound(Class[])
@@ -125,9 +126,7 @@ public class Jaxb2Marshaller
125126 private static final String CID = "cid:" ;
126127
127128
128- /**
129- * Logger available to subclasses.
130- */
129+ /** Logger available to subclasses */
131130 protected final Log logger = LogFactory .getLog (getClass ());
132131
133132 private String contextPath ;
@@ -154,21 +153,25 @@ public class Jaxb2Marshaller
154153
155154 private String schemaLanguage = XMLConstants .W3C_XML_SCHEMA_NS_URI ;
156155
156+ private LSResourceResolver schemaResourceResolver ;
157+
157158 private boolean mtomEnabled = false ;
158159
159- private ClassLoader beanClassLoader ;
160+ private boolean lazyInit = false ;
160161
161- private ResourceLoader resourceLoader ;
162+ private boolean supportJaxbElementClass = false ;
162163
163- private JAXBContext jaxbContext ;
164+ private boolean checkForXmlRootElement = true ;
164165
165- private Schema schema ;
166+ private ClassLoader beanClassLoader ;
166167
167- private boolean lazyInit = false ;
168+ private ResourceLoader resourceLoader ;
168169
169- private boolean supportJaxbElementClass = false ;
170+ private final Object jaxbContextMonitor = new Object () ;
170171
171- private LSResourceResolver schemaResourceResolver ;
172+ private volatile JAXBContext jaxbContext ;
173+
174+ private Schema schema ;
172175
173176
174177 /**
@@ -358,6 +361,21 @@ public void setSupportJaxbElementClass(boolean supportJaxbElementClass) {
358361 this .supportJaxbElementClass = supportJaxbElementClass ;
359362 }
360363
364+ /**
365+ * Specify whether the {@link #supports(Class)} should check for
366+ * {@link XmlRootElement @XmlRootElement} annotations.
367+ * <p>Default is {@code true}, meaning that {@code supports(Class)} will check for
368+ * this annotation. However, some JAXB implementations (i.e. EclipseLink MOXy) allow
369+ * for defining the bindings in an external definition file, thus keeping the classes
370+ * annotations free. Setting this property to {@code false} supports these
371+ * JAXB implementations.
372+ * @see #supports(Class)
373+ * @see #supports(Type)
374+ */
375+ public void setCheckForXmlRootElement (boolean checkForXmlRootElement ) {
376+ this .checkForXmlRootElement = checkForXmlRootElement ;
377+ }
378+
361379 public void setBeanClassLoader (ClassLoader classLoader ) {
362380 this .beanClassLoader = classLoader ;
363381 }
@@ -388,24 +406,29 @@ public final void afterPropertiesSet() throws Exception {
388406 }
389407 }
390408
391- protected synchronized JAXBContext getJaxbContext () {
392- if (this .jaxbContext == null ) {
393- try {
394- if (StringUtils .hasLength (this .contextPath )) {
395- this .jaxbContext = createJaxbContextFromContextPath ();
396- }
397- else if (!ObjectUtils .isEmpty (this .classesToBeBound )) {
398- this .jaxbContext = createJaxbContextFromClasses ();
409+ protected JAXBContext getJaxbContext () {
410+ if (this .jaxbContext != null ) {
411+ return this .jaxbContext ;
412+ }
413+ synchronized (this .jaxbContextMonitor ) {
414+ if (this .jaxbContext == null ) {
415+ try {
416+ if (StringUtils .hasLength (this .contextPath )) {
417+ this .jaxbContext = createJaxbContextFromContextPath ();
418+ }
419+ else if (!ObjectUtils .isEmpty (this .classesToBeBound )) {
420+ this .jaxbContext = createJaxbContextFromClasses ();
421+ }
422+ else if (!ObjectUtils .isEmpty (this .packagesToScan )) {
423+ this .jaxbContext = createJaxbContextFromPackages ();
424+ }
399425 }
400- else if (! ObjectUtils . isEmpty ( this . packagesToScan ) ) {
401- this . jaxbContext = createJaxbContextFromPackages ( );
426+ catch ( JAXBException ex ) {
427+ throw convertJaxbException ( ex );
402428 }
403429 }
404- catch (JAXBException ex ) {
405- throw convertJaxbException (ex );
406- }
430+ return this .jaxbContext ;
407431 }
408- return this .jaxbContext ;
409432 }
410433
411434 private JAXBContext createJaxbContextFromContextPath () throws JAXBException {
@@ -417,7 +440,9 @@ private JAXBContext createJaxbContextFromContextPath() throws JAXBException {
417440 return JAXBContext .newInstance (this .contextPath , this .beanClassLoader , this .jaxbContextProperties );
418441 }
419442 else {
420- return JAXBContext .newInstance (this .contextPath , ClassUtils .getDefaultClassLoader (), this .jaxbContextProperties );
443+ // analogous to the JAXBContext.newInstance(String) implementation
444+ return JAXBContext .newInstance (this .contextPath , Thread .currentThread ().getContextClassLoader (),
445+ this .jaxbContextProperties );
421446 }
422447 }
423448 else {
@@ -492,7 +517,7 @@ public boolean supports(Class<?> clazz) {
492517 if (this .supportJaxbElementClass && JAXBElement .class .isAssignableFrom (clazz )) {
493518 return true ;
494519 }
495- return supportsInternal (clazz , true );
520+ return supportsInternal (clazz , this . checkForXmlRootElement );
496521 }
497522
498523 public boolean supports (Type genericType ) {
@@ -521,7 +546,7 @@ else if (JdkVersion.getMajorJavaVersion() <= JdkVersion.JAVA_16 &&
521546 }
522547 else if (genericType instanceof Class ) {
523548 Class <?> clazz = (Class <?>) genericType ;
524- return supportsInternal (clazz , true );
549+ return supportsInternal (clazz , this . checkForXmlRootElement );
525550 }
526551 return false ;
527552 }
0 commit comments