From 682b0f2c63791f2cfe2aa0fd2978f11d97ea545b Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Thu, 11 Oct 2012 22:31:48 +0200 Subject: [PATCH] Jaxb2Marshaller has non-synchronized access to the JAXBContext once initialized Also backporting "checkForXmlRootElement" property from 3.2 M2 Issue: SPR-9867 Issue: SPR-9757 --- .../oxm/jaxb/Jaxb2Marshaller.java | 95 ++++++++++++------- 1 file changed, 60 insertions(+), 35 deletions(-) diff --git a/org.springframework.oxm/src/main/java/org/springframework/oxm/jaxb/Jaxb2Marshaller.java b/org.springframework.oxm/src/main/java/org/springframework/oxm/jaxb/Jaxb2Marshaller.java index dbfb260db2..a53bddf3f0 100644 --- a/org.springframework.oxm/src/main/java/org/springframework/oxm/jaxb/Jaxb2Marshaller.java +++ b/org.springframework.oxm/src/main/java/org/springframework/oxm/jaxb/Jaxb2Marshaller.java @@ -101,11 +101,12 @@ import org.springframework.util.xml.StaxUtils; /** * Implementation of the Marshaller interface for JAXB 2.0. * - *

The typical usage will be to set either the contextPath or the classesToBeBound property - * on this bean, possibly customize the marshaller and unmarshaller by setting properties, schemas, adapters, and - * listeners, and to refer to it. + *

The typical usage will be to set either the "contextPath" or the "classesToBeBound" + * property on this bean, possibly customize the marshaller and unmarshaller by setting + * properties, schemas, adapters, and listeners, and to refer to it. * * @author Arjen Poutsma + * @author Juergen Hoeller * @since 3.0 * @see #setContextPath(String) * @see #setClassesToBeBound(Class[]) @@ -125,9 +126,7 @@ public class Jaxb2Marshaller private static final String CID = "cid:"; - /** - * Logger available to subclasses. - */ + /** Logger available to subclasses */ protected final Log logger = LogFactory.getLog(getClass()); private String contextPath; @@ -154,21 +153,25 @@ public class Jaxb2Marshaller private String schemaLanguage = XMLConstants.W3C_XML_SCHEMA_NS_URI; + private LSResourceResolver schemaResourceResolver; + private boolean mtomEnabled = false; - private ClassLoader beanClassLoader; - - private ResourceLoader resourceLoader; - - private JAXBContext jaxbContext; - - private Schema schema; - private boolean lazyInit = false; private boolean supportJaxbElementClass = false; - private LSResourceResolver schemaResourceResolver; + private boolean checkForXmlRootElement = true; + + private ClassLoader beanClassLoader; + + private ResourceLoader resourceLoader; + + private final Object jaxbContextMonitor = new Object(); + + private volatile JAXBContext jaxbContext; + + private Schema schema; /** @@ -358,6 +361,21 @@ public class Jaxb2Marshaller this.supportJaxbElementClass = supportJaxbElementClass; } + /** + * Specify whether the {@link #supports(Class)} should check for + * {@link XmlRootElement @XmlRootElement} annotations. + *

Default is {@code true}, meaning that {@code supports(Class)} will check for + * this annotation. However, some JAXB implementations (i.e. EclipseLink MOXy) allow + * for defining the bindings in an external definition file, thus keeping the classes + * annotations free. Setting this property to {@code false} supports these + * JAXB implementations. + * @see #supports(Class) + * @see #supports(Type) + */ + public void setCheckForXmlRootElement(boolean checkForXmlRootElement) { + this.checkForXmlRootElement = checkForXmlRootElement; + } + public void setBeanClassLoader(ClassLoader classLoader) { this.beanClassLoader = classLoader; } @@ -388,24 +406,29 @@ public class Jaxb2Marshaller } } - protected synchronized JAXBContext getJaxbContext() { - if (this.jaxbContext == null) { - try { - if (StringUtils.hasLength(this.contextPath)) { - this.jaxbContext = createJaxbContextFromContextPath(); - } - else if (!ObjectUtils.isEmpty(this.classesToBeBound)) { - this.jaxbContext = createJaxbContextFromClasses(); - } - else if (!ObjectUtils.isEmpty(this.packagesToScan)) { - this.jaxbContext = createJaxbContextFromPackages(); - } - } - catch (JAXBException ex) { - throw convertJaxbException(ex); - } + protected JAXBContext getJaxbContext() { + if (this.jaxbContext != null) { + return this.jaxbContext; + } + synchronized (this.jaxbContextMonitor) { + if (this.jaxbContext == null) { + try { + if (StringUtils.hasLength(this.contextPath)) { + this.jaxbContext = createJaxbContextFromContextPath(); + } + else if (!ObjectUtils.isEmpty(this.classesToBeBound)) { + this.jaxbContext = createJaxbContextFromClasses(); + } + else if (!ObjectUtils.isEmpty(this.packagesToScan)) { + this.jaxbContext = createJaxbContextFromPackages(); + } + } + catch (JAXBException ex) { + throw convertJaxbException(ex); + } + } + return this.jaxbContext; } - return this.jaxbContext; } private JAXBContext createJaxbContextFromContextPath() throws JAXBException { @@ -417,7 +440,9 @@ public class Jaxb2Marshaller return JAXBContext.newInstance(this.contextPath, this.beanClassLoader, this.jaxbContextProperties); } else { - return JAXBContext.newInstance(this.contextPath, ClassUtils.getDefaultClassLoader(), this.jaxbContextProperties); + // analogous to the JAXBContext.newInstance(String) implementation + return JAXBContext.newInstance(this.contextPath, Thread.currentThread().getContextClassLoader(), + this.jaxbContextProperties); } } else { @@ -492,7 +517,7 @@ public class Jaxb2Marshaller if (this.supportJaxbElementClass && JAXBElement.class.isAssignableFrom(clazz)) { return true; } - return supportsInternal(clazz, true); + return supportsInternal(clazz, this.checkForXmlRootElement); } public boolean supports(Type genericType) { @@ -521,7 +546,7 @@ public class Jaxb2Marshaller } else if (genericType instanceof Class) { Class clazz = (Class) genericType; - return supportsInternal(clazz, true); + return supportsInternal(clazz, this.checkForXmlRootElement); } return false; }