Skip to content

Commit 682b0f2

Browse files
committed
Jaxb2Marshaller has non-synchronized access to the JAXBContext once initialized
Also backporting "checkForXmlRootElement" property from 3.2 M2 Issue: SPR-9867 Issue: SPR-9757
1 parent ad81ec9 commit 682b0f2

File tree

1 file changed

+55
-30
lines changed

1 file changed

+55
-30
lines changed

org.springframework.oxm/src/main/java/org/springframework/oxm/jaxb/Jaxb2Marshaller.java

Lines changed: 55 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -101,11 +101,12 @@
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

Comments
 (0)