diff --git a/spring-oxm/src/main/java/org/springframework/oxm/jaxb/Jaxb2Marshaller.java b/spring-oxm/src/main/java/org/springframework/oxm/jaxb/Jaxb2Marshaller.java
index c5ef0e84b8..d1bc12f038 100644
--- a/spring-oxm/src/main/java/org/springframework/oxm/jaxb/Jaxb2Marshaller.java
+++ b/spring-oxm/src/main/java/org/springframework/oxm/jaxb/Jaxb2Marshaller.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2014 the original author or authors.
+ * Copyright 2002-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -176,6 +176,8 @@ public class Jaxb2Marshaller implements MimeMarshaller, MimeUnmarshaller, Generi
private Schema schema;
+ private boolean supportDtd = false;
+
private boolean processExternalEntities = false;
@@ -390,6 +392,21 @@ public class Jaxb2Marshaller implements MimeMarshaller, MimeUnmarshaller, Generi
this.mappedClass = mappedClass;
}
+ /**
+ * Indicates whether DTD parsing should be supported.
+ *
Default is {@code false} meaning that DTD is disabled.
+ */
+ public void setSupportDtd(boolean supportDtd) {
+ this.supportDtd = supportDtd;
+ }
+
+ /**
+ * Whether DTD parsing is supported.
+ */
+ public boolean isSupportDtd() {
+ return this.supportDtd;
+ }
+
/**
* Indicates whether external XML entities are processed when unmarshalling.
*
Default is {@code false}, meaning that external entities are not resolved.
@@ -397,9 +414,14 @@ public class Jaxb2Marshaller implements MimeMarshaller, MimeUnmarshaller, Generi
* {@code Source} passed to {@link #unmarshal(Source)} is a {@link SAXSource} or
* {@link StreamSource}. It has no effect for {@link DOMSource} or {@link StAXSource}
* instances.
+ *
Note: setting this option to {@code true} also
+ * automatically sets {@link #setSupportDtd} to {@code true}.
*/
public void setProcessExternalEntities(boolean processExternalEntities) {
this.processExternalEntities = processExternalEntities;
+ if (processExternalEntities) {
+ setSupportDtd(true);
+ }
}
/**
@@ -746,6 +768,14 @@ public class Jaxb2Marshaller implements MimeMarshaller, MimeUnmarshaller, Generi
return unmarshaller.unmarshal(source);
}
}
+ catch (NullPointerException ex) {
+ if (!isSupportDtd()) {
+ throw new UnmarshallingFailureException("NPE while unmarshalling. " +
+ "This can happen on JDK 1.6 due to the presence of DTD " +
+ "declarations, which are disabled.", ex);
+ }
+ throw ex;
+ }
catch (JAXBException ex) {
throw convertJaxbException(ex);
}
@@ -801,6 +831,7 @@ public class Jaxb2Marshaller implements MimeMarshaller, MimeUnmarshaller, Generi
if (xmlReader == null) {
xmlReader = XMLReaderFactory.createXMLReader();
}
+ xmlReader.setFeature("http://apache.org/xml/features/disallow-doctype-decl", !isSupportDtd());
String name = "http://xml.org/sax/features/external-general-entities";
xmlReader.setFeature(name, isProcessExternalEntities());
if (!isProcessExternalEntities()) {
diff --git a/spring-oxm/src/main/java/org/springframework/oxm/support/AbstractMarshaller.java b/spring-oxm/src/main/java/org/springframework/oxm/support/AbstractMarshaller.java
index 7c090750f1..de37a6002b 100644
--- a/spring-oxm/src/main/java/org/springframework/oxm/support/AbstractMarshaller.java
+++ b/spring-oxm/src/main/java/org/springframework/oxm/support/AbstractMarshaller.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2014 the original author or authors.
+ * Copyright 2002-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -71,6 +71,8 @@ public abstract class AbstractMarshaller implements Marshaller, Unmarshaller {
/** Logger available to subclasses */
protected final Log logger = LogFactory.getLog(getClass());
+ private boolean supportDtd = false;
+
private boolean processExternalEntities = false;
private DocumentBuilderFactory documentBuilderFactory;
@@ -78,6 +80,21 @@ public abstract class AbstractMarshaller implements Marshaller, Unmarshaller {
private final Object documentBuilderFactoryMonitor = new Object();
+ /**
+ * Indicates whether DTD parsing should be supported.
+ *
Default is {@code false} meaning that DTD is disabled.
+ */
+ public void setSupportDtd(boolean supportDtd) {
+ this.supportDtd = supportDtd;
+ }
+
+ /**
+ * Whether DTD parsing is supported.
+ */
+ public boolean isSupportDtd() {
+ return this.supportDtd;
+ }
+
/**
* Indicates whether external XML entities are processed when unmarshalling.
*
Default is {@code false}, meaning that external entities are not resolved.
@@ -85,9 +102,14 @@ public abstract class AbstractMarshaller implements Marshaller, Unmarshaller {
* {@code Source} passed to {@link #unmarshal(Source)} is a {@link SAXSource} or
* {@link StreamSource}. It has no effect for {@link DOMSource} or {@link StAXSource}
* instances.
+ *
Note: setting this option to {@code true} also
+ * automatically sets {@link #setSupportDtd} to {@code true}.
*/
public void setProcessExternalEntities(boolean processExternalEntities) {
this.processExternalEntities = processExternalEntities;
+ if (processExternalEntities) {
+ setSupportDtd(true);
+ }
}
/**
@@ -111,6 +133,8 @@ public abstract class AbstractMarshaller implements Marshaller, Unmarshaller {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setValidating(false);
factory.setNamespaceAware(true);
+ factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", !isSupportDtd());
+ factory.setFeature("http://xml.org/sax/features/external-general-entities", isProcessExternalEntities());
return factory;
}
@@ -125,7 +149,11 @@ public abstract class AbstractMarshaller implements Marshaller, Unmarshaller {
protected DocumentBuilder createDocumentBuilder(DocumentBuilderFactory factory)
throws ParserConfigurationException {
- return factory.newDocumentBuilder();
+ DocumentBuilder documentBuilder = factory.newDocumentBuilder();
+ if (!isProcessExternalEntities()) {
+ documentBuilder.setEntityResolver(NO_OP_ENTITY_RESOLVER);
+ }
+ return documentBuilder;
}
/**
@@ -135,6 +163,7 @@ public abstract class AbstractMarshaller implements Marshaller, Unmarshaller {
*/
protected XMLReader createXmlReader() throws SAXException {
XMLReader xmlReader = XMLReaderFactory.createXMLReader();
+ xmlReader.setFeature("http://apache.org/xml/features/disallow-doctype-decl", !isSupportDtd());
xmlReader.setFeature("http://xml.org/sax/features/external-general-entities", isProcessExternalEntities());
if (!isProcessExternalEntities()) {
xmlReader.setEntityResolver(NO_OP_ENTITY_RESOLVER);
@@ -343,7 +372,17 @@ public abstract class AbstractMarshaller implements Marshaller, Unmarshaller {
"Could not create document placeholder for DOMSource: " + ex.getMessage(), ex);
}
}
- return unmarshalDomNode(domSource.getNode());
+ try {
+ return unmarshalDomNode(domSource.getNode());
+ }
+ catch (NullPointerException ex) {
+ if (!isSupportDtd()) {
+ throw new UnmarshallingFailureException("NPE while unmarshalling. " +
+ "This can happen on JDK 1.6 due to the presence of DTD " +
+ "declarations, which are disabled.", ex);
+ }
+ throw ex;
+ }
}
/**
@@ -391,7 +430,17 @@ public abstract class AbstractMarshaller implements Marshaller, Unmarshaller {
if (saxSource.getInputSource() == null) {
saxSource.setInputSource(new InputSource());
}
- return unmarshalSaxReader(saxSource.getXMLReader(), saxSource.getInputSource());
+ try {
+ return unmarshalSaxReader(saxSource.getXMLReader(), saxSource.getInputSource());
+ }
+ catch (NullPointerException ex) {
+ if (!isSupportDtd()) {
+ throw new UnmarshallingFailureException("NPE while unmarshalling. " +
+ "This can happen on JDK 1.6 due to the presence of DTD " +
+ "declarations, which are disabled.", ex);
+ }
+ throw ex;
+ }
}
/**
@@ -404,7 +453,7 @@ public abstract class AbstractMarshaller implements Marshaller, Unmarshaller {
*/
protected Object unmarshalStreamSource(StreamSource streamSource) throws XmlMappingException, IOException {
if (streamSource.getInputStream() != null) {
- if (isProcessExternalEntities()) {
+ if (isProcessExternalEntities() && isSupportDtd()) {
return unmarshalInputStream(streamSource.getInputStream());
}
else {
@@ -414,7 +463,7 @@ public abstract class AbstractMarshaller implements Marshaller, Unmarshaller {
}
}
else if (streamSource.getReader() != null) {
- if (isProcessExternalEntities()) {
+ if (isProcessExternalEntities() && isSupportDtd()) {
return unmarshalReader(streamSource.getReader());
}
else {
diff --git a/spring-oxm/src/test/java/org/springframework/oxm/castor/CastorUnmarshallerTests.java b/spring-oxm/src/test/java/org/springframework/oxm/castor/CastorUnmarshallerTests.java
index 374df68faa..ce2f1c7caa 100644
--- a/spring-oxm/src/test/java/org/springframework/oxm/castor/CastorUnmarshallerTests.java
+++ b/spring-oxm/src/test/java/org/springframework/oxm/castor/CastorUnmarshallerTests.java
@@ -206,21 +206,24 @@ public class CastorUnmarshallerTests extends AbstractUnmarshallerTests {
}
};
- // 1. external-general-entities disabled (default)
+ // 1. external-general-entities and dtd support disabled (default)
marshaller.unmarshal(new StreamSource("1"));
assertNotNull(result.get());
+ assertEquals(true, result.get().getFeature("http://apache.org/xml/features/disallow-doctype-decl"));
assertEquals(false, result.get().getFeature("http://xml.org/sax/features/external-general-entities"));
- // 2. external-general-entities disabled (default)
+ // 2. external-general-entities and dtd support enabled
result.set(null);
+ marshaller.setSupportDtd(true);
marshaller.setProcessExternalEntities(true);
marshaller.unmarshal(new StreamSource("1"));
assertNotNull(result.get());
+ assertEquals(false, result.get().getFeature("http://apache.org/xml/features/disallow-doctype-decl"));
assertEquals(true, result.get().getFeature("http://xml.org/sax/features/external-general-entities"));
}
@Test
- public void unmarshalSaxSourceExternalEntities() throws Exception {
+ public void unmarshalSaxSourceWithXmlOptions() throws Exception {
final AtomicReference result = new AtomicReference();
CastorMarshaller marshaller = new CastorMarshaller() {
@Override
@@ -230,16 +233,19 @@ public class CastorUnmarshallerTests extends AbstractUnmarshallerTests {
}
};
- // 1. external-general-entities disabled (default)
+ // 1. external-general-entities and dtd support disabled (default)
marshaller.unmarshal(new SAXSource(new InputSource("1")));
assertNotNull(result.get());
+ assertEquals(true, result.get().getFeature("http://apache.org/xml/features/disallow-doctype-decl"));
assertEquals(false, result.get().getFeature("http://xml.org/sax/features/external-general-entities"));
- // 2. external-general-entities disabled (default)
+ // 2. external-general-entities and dtd support enabled
result.set(null);
+ marshaller.setSupportDtd(true);
marshaller.setProcessExternalEntities(true);
marshaller.unmarshal(new SAXSource(new InputSource("1")));
assertNotNull(result.get());
+ assertEquals(false, result.get().getFeature("http://apache.org/xml/features/disallow-doctype-decl"));
assertEquals(true, result.get().getFeature("http://xml.org/sax/features/external-general-entities"));
}
diff --git a/spring-oxm/src/test/java/org/springframework/oxm/jaxb/Jaxb2MarshallerTests.java b/spring-oxm/src/test/java/org/springframework/oxm/jaxb/Jaxb2MarshallerTests.java
index 5e73404bc3..60d69bb9d6 100644
--- a/spring-oxm/src/test/java/org/springframework/oxm/jaxb/Jaxb2MarshallerTests.java
+++ b/spring-oxm/src/test/java/org/springframework/oxm/jaxb/Jaxb2MarshallerTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2014 the original author or authors.
+ * Copyright 2002-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -308,7 +308,7 @@ public class Jaxb2MarshallerTests extends AbstractMarshallerTests {
// SPR-10806
@Test
- public void unmarshalStreamSourceExternalEntities() throws Exception {
+ public void unmarshalStreamSourceWithXmlOptions() throws Exception {
final javax.xml.bind.Unmarshaller unmarshaller = mock(javax.xml.bind.Unmarshaller.class);
Jaxb2Marshaller marshaller = new Jaxb2Marshaller() {
@@ -318,24 +318,27 @@ public class Jaxb2MarshallerTests extends AbstractMarshallerTests {
}
};
- // 1. external-general-entities disabled (default)
+ // 1. external-general-entities and dtd support disabled (default)
marshaller.unmarshal(new StreamSource("1"));
ArgumentCaptor sourceCaptor = ArgumentCaptor.forClass(SAXSource.class);
verify(unmarshaller).unmarshal(sourceCaptor.capture());
SAXSource result = sourceCaptor.getValue();
+ assertEquals(true, result.getXMLReader().getFeature("http://apache.org/xml/features/disallow-doctype-decl"));
assertEquals(false, result.getXMLReader().getFeature("http://xml.org/sax/features/external-general-entities"));
- // 2. external-general-entities enabled
+ // 2. external-general-entities and dtd support enabled
reset(unmarshaller);
+ marshaller.setSupportDtd(true);
marshaller.setProcessExternalEntities(true);
marshaller.unmarshal(new StreamSource("1"));
verify(unmarshaller).unmarshal(sourceCaptor.capture());
result = sourceCaptor.getValue();
+ assertEquals(false, result.getXMLReader().getFeature("http://apache.org/xml/features/disallow-doctype-decl"));
assertEquals(true, result.getXMLReader().getFeature("http://xml.org/sax/features/external-general-entities"));
}
@@ -352,24 +355,27 @@ public class Jaxb2MarshallerTests extends AbstractMarshallerTests {
}
};
- // 1. external-general-entities disabled (default)
+ // 1. external-general-entities and dtd support disabled (default)
marshaller.unmarshal(new SAXSource(new InputSource("1")));
ArgumentCaptor sourceCaptor = ArgumentCaptor.forClass(SAXSource.class);
verify(unmarshaller).unmarshal(sourceCaptor.capture());
SAXSource result = sourceCaptor.getValue();
+ assertEquals(true, result.getXMLReader().getFeature("http://apache.org/xml/features/disallow-doctype-decl"));
assertEquals(false, result.getXMLReader().getFeature("http://xml.org/sax/features/external-general-entities"));
- // 2. external-general-entities enabled
+ // 2. external-general-entities and dtd support enabled
reset(unmarshaller);
+ marshaller.setSupportDtd(true);
marshaller.setProcessExternalEntities(true);
marshaller.unmarshal(new SAXSource(new InputSource("1")));
verify(unmarshaller).unmarshal(sourceCaptor.capture());
result = sourceCaptor.getValue();
+ assertEquals(false, result.getXMLReader().getFeature("http://apache.org/xml/features/disallow-doctype-decl"));
assertEquals(true, result.getXMLReader().getFeature("http://xml.org/sax/features/external-general-entities"));
}
diff --git a/spring-web/src/main/java/org/springframework/http/converter/xml/Jaxb2CollectionHttpMessageConverter.java b/spring-web/src/main/java/org/springframework/http/converter/xml/Jaxb2CollectionHttpMessageConverter.java
index 4ff27e06ac..07a4b7d13f 100644
--- a/spring-web/src/main/java/org/springframework/http/converter/xml/Jaxb2CollectionHttpMessageConverter.java
+++ b/spring-web/src/main/java/org/springframework/http/converter/xml/Jaxb2CollectionHttpMessageConverter.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2014 the original author or authors.
+ * Copyright 2002-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -226,6 +226,7 @@ public class Jaxb2CollectionHttpMessageConverter
*/
protected XMLInputFactory createXmlInputFactory() {
XMLInputFactory inputFactory = XMLInputFactory.newInstance();
+ inputFactory.setProperty(XMLInputFactory.SUPPORT_DTD, false);
inputFactory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false);
inputFactory.setXMLResolver(NO_OP_XML_RESOLVER);
return inputFactory;
diff --git a/spring-web/src/main/java/org/springframework/http/converter/xml/Jaxb2RootElementHttpMessageConverter.java b/spring-web/src/main/java/org/springframework/http/converter/xml/Jaxb2RootElementHttpMessageConverter.java
index b6ff303472..2decf67bd9 100644
--- a/spring-web/src/main/java/org/springframework/http/converter/xml/Jaxb2RootElementHttpMessageConverter.java
+++ b/spring-web/src/main/java/org/springframework/http/converter/xml/Jaxb2RootElementHttpMessageConverter.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2014 the original author or authors.
+ * Copyright 2002-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -60,15 +60,37 @@ import org.springframework.util.ClassUtils;
*/
public class Jaxb2RootElementHttpMessageConverter extends AbstractJaxb2HttpMessageConverter