AbstractMarshaller pre-implements getDefaultEncoding() method
Issue: SPR-11635
This commit is contained in:
@@ -40,6 +40,7 @@ import javax.xml.transform.stream.StreamSource;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Node;
|
||||
import org.xml.sax.ContentHandler;
|
||||
import org.xml.sax.InputSource;
|
||||
@@ -66,15 +67,15 @@ import org.springframework.util.xml.StaxUtils;
|
||||
*/
|
||||
public abstract class AbstractMarshaller implements Marshaller, Unmarshaller {
|
||||
|
||||
/** Logger available to subclasses. */
|
||||
/** Logger available to subclasses */
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private boolean processExternalEntities = false;
|
||||
|
||||
private DocumentBuilderFactory documentBuilderFactory;
|
||||
|
||||
private final Object documentBuilderFactoryMonitor = new Object();
|
||||
|
||||
private boolean processExternalEntities = false;
|
||||
|
||||
|
||||
/**
|
||||
* Indicates whether external XML entities are processed when unmarshalling.
|
||||
@@ -89,18 +90,85 @@ public abstract class AbstractMarshaller implements Marshaller, Unmarshaller {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the configured value for whether XML external entities are allowed.
|
||||
* Returns the configured value for whether XML external entities are allowed.
|
||||
* @see #createXmlReader()
|
||||
*/
|
||||
public boolean isProcessExternalEntities() {
|
||||
return this.processExternalEntities;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the default encoding to use for marshalling or unmarshalling from
|
||||
* a byte stream, or {@code null}.
|
||||
*/
|
||||
abstract protected String getDefaultEncoding();
|
||||
|
||||
/**
|
||||
* Build a new {@link Document} from this marshaller's {@link DocumentBuilderFactory}.
|
||||
* @see #createDocumentBuilderFactory()
|
||||
* @see #createDocumentBuilder(DocumentBuilderFactory)
|
||||
*/
|
||||
protected Document buildDocument() {
|
||||
try {
|
||||
synchronized (this.documentBuilderFactoryMonitor) {
|
||||
if (this.documentBuilderFactory == null) {
|
||||
this.documentBuilderFactory = createDocumentBuilderFactory();
|
||||
}
|
||||
}
|
||||
DocumentBuilder documentBuilder = createDocumentBuilder(this.documentBuilderFactory);
|
||||
return documentBuilder.newDocument();
|
||||
}
|
||||
catch (ParserConfigurationException ex) {
|
||||
throw new UnmarshallingFailureException(
|
||||
"Could not create document placeholder for DOMSource: " + ex.getMessage(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@code DocumentBuilder} that this marshaller will use for creating
|
||||
* DOM documents when passed an empty {@code DOMSource}.
|
||||
* <p>The resulting {@code DocumentBuilderFactory} is cached, so this method
|
||||
* will only be called once.
|
||||
* @return the DocumentBuilderFactory
|
||||
* @throws ParserConfigurationException if thrown by JAXP methods
|
||||
*/
|
||||
protected DocumentBuilderFactory createDocumentBuilderFactory() throws ParserConfigurationException {
|
||||
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
||||
factory.setValidating(false);
|
||||
factory.setNamespaceAware(true);
|
||||
return factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@code DocumentBuilder} that this marshaller will use for creating
|
||||
* DOM documents when passed an empty {@code DOMSource}.
|
||||
* <p>Can be overridden in subclasses, adding further initialization of the builder.
|
||||
* @param factory the {@code DocumentBuilderFactory} that the DocumentBuilder should be created with
|
||||
* @return the {@code DocumentBuilder}
|
||||
* @throws ParserConfigurationException if thrown by JAXP methods
|
||||
*/
|
||||
protected DocumentBuilder createDocumentBuilder(DocumentBuilderFactory factory)
|
||||
throws ParserConfigurationException {
|
||||
|
||||
return factory.newDocumentBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an {@code XMLReader} that this marshaller will when passed an empty {@code SAXSource}.
|
||||
* @return the XMLReader
|
||||
* @throws SAXException if thrown by JAXP methods
|
||||
*/
|
||||
protected XMLReader createXmlReader() throws SAXException {
|
||||
XMLReader xmlReader = XMLReaderFactory.createXMLReader();
|
||||
xmlReader.setFeature("http://xml.org/sax/features/external-general-entities", isProcessExternalEntities());
|
||||
return xmlReader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the default encoding to use for marshalling or unmarshalling from
|
||||
* a byte stream, or {@code null} if none.
|
||||
*/
|
||||
protected String getDefaultEncoding() {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
// Marshalling
|
||||
|
||||
/**
|
||||
* Marshals the object graph with the given root into the provided {@code javax.xml.transform.Result}.
|
||||
@@ -135,82 +203,6 @@ public abstract class AbstractMarshaller implements Marshaller, Unmarshaller {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unmarshals the given provided {@code javax.xml.transform.Source} into an object graph.
|
||||
* <p>This implementation inspects the given result, and calls {@code unmarshalDomSource},
|
||||
* {@code unmarshalSaxSource}, or {@code unmarshalStreamSource}.
|
||||
* @param source the source to marshal from
|
||||
* @return the object graph
|
||||
* @throws IOException if an I/O Exception occurs
|
||||
* @throws XmlMappingException if the given source cannot be mapped to an object
|
||||
* @throws IllegalArgumentException if {@code source} is neither a {@code DOMSource},
|
||||
* a {@code SAXSource}, nor a {@code StreamSource}
|
||||
* @see #unmarshalDomSource(javax.xml.transform.dom.DOMSource)
|
||||
* @see #unmarshalSaxSource(javax.xml.transform.sax.SAXSource)
|
||||
* @see #unmarshalStreamSource(javax.xml.transform.stream.StreamSource)
|
||||
*/
|
||||
@Override
|
||||
public final Object unmarshal(Source source) throws IOException, XmlMappingException {
|
||||
if (source instanceof DOMSource) {
|
||||
return unmarshalDomSource((DOMSource) source);
|
||||
}
|
||||
else if (StaxUtils.isStaxSource(source)) {
|
||||
return unmarshalStaxSource(source);
|
||||
}
|
||||
else if (source instanceof SAXSource) {
|
||||
return unmarshalSaxSource((SAXSource) source);
|
||||
}
|
||||
else if (source instanceof StreamSource) {
|
||||
return unmarshalStreamSourceNoExternalEntitities((StreamSource) source);
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("Unknown Source type: " + source.getClass());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@code DocumentBuilder} that this marshaller will use for creating
|
||||
* DOM documents when passed an empty {@code DOMSource}.
|
||||
* <p>Can be overridden in subclasses, adding further initialization of the builder.
|
||||
* @param factory the {@code DocumentBuilderFactory} that the DocumentBuilder should be created with
|
||||
* @return the {@code DocumentBuilder}
|
||||
* @throws ParserConfigurationException if thrown by JAXP methods
|
||||
*/
|
||||
protected DocumentBuilder createDocumentBuilder(DocumentBuilderFactory factory)
|
||||
throws ParserConfigurationException {
|
||||
|
||||
return factory.newDocumentBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@code DocumentBuilder} that this marshaller will use for creating
|
||||
* DOM documents when passed an empty {@code DOMSource}.
|
||||
* <p>The resulting {@code DocumentBuilderFactory} is cached, so this method
|
||||
* will only be called once.
|
||||
* @return the DocumentBuilderFactory
|
||||
* @throws ParserConfigurationException if thrown by JAXP methods
|
||||
*/
|
||||
protected DocumentBuilderFactory createDocumentBuilderFactory() throws ParserConfigurationException {
|
||||
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
||||
factory.setValidating(false);
|
||||
factory.setNamespaceAware(true);
|
||||
return factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an {@code XMLReader} that this marshaller will when passed an empty {@code SAXSource}.
|
||||
* @return the XMLReader
|
||||
* @throws SAXException if thrown by JAXP methods
|
||||
*/
|
||||
protected XMLReader createXmlReader() throws SAXException {
|
||||
XMLReader xmlReader = XMLReaderFactory.createXMLReader();
|
||||
xmlReader.setFeature("http://xml.org/sax/features/external-general-entities", isProcessExternalEntities());
|
||||
return xmlReader;
|
||||
}
|
||||
|
||||
|
||||
// Marshalling
|
||||
|
||||
/**
|
||||
* Template method for handling {@code DOMResult}s.
|
||||
* <p>This implementation delegates to {@code marshalDomNode}.
|
||||
@@ -222,19 +214,7 @@ public abstract class AbstractMarshaller implements Marshaller, Unmarshaller {
|
||||
*/
|
||||
protected void marshalDomResult(Object graph, DOMResult domResult) throws XmlMappingException {
|
||||
if (domResult.getNode() == null) {
|
||||
try {
|
||||
synchronized (this.documentBuilderFactoryMonitor) {
|
||||
if (this.documentBuilderFactory == null) {
|
||||
this.documentBuilderFactory = createDocumentBuilderFactory();
|
||||
}
|
||||
}
|
||||
DocumentBuilder documentBuilder = createDocumentBuilder(this.documentBuilderFactory);
|
||||
domResult.setNode(documentBuilder.newDocument());
|
||||
}
|
||||
catch (ParserConfigurationException ex) {
|
||||
throw new UnmarshallingFailureException(
|
||||
"Could not create document placeholder for DOMResult: " + ex.getMessage(), ex);
|
||||
}
|
||||
domResult.setNode(buildDocument());
|
||||
}
|
||||
marshalDomNode(graph, domResult.getNode());
|
||||
}
|
||||
@@ -309,6 +289,39 @@ public abstract class AbstractMarshaller implements Marshaller, Unmarshaller {
|
||||
|
||||
// Unmarshalling
|
||||
|
||||
/**
|
||||
* Unmarshals the given provided {@code javax.xml.transform.Source} into an object graph.
|
||||
* <p>This implementation inspects the given result, and calls {@code unmarshalDomSource},
|
||||
* {@code unmarshalSaxSource}, or {@code unmarshalStreamSource}.
|
||||
* @param source the source to marshal from
|
||||
* @return the object graph
|
||||
* @throws IOException if an I/O Exception occurs
|
||||
* @throws XmlMappingException if the given source cannot be mapped to an object
|
||||
* @throws IllegalArgumentException if {@code source} is neither a {@code DOMSource},
|
||||
* a {@code SAXSource}, nor a {@code StreamSource}
|
||||
* @see #unmarshalDomSource(javax.xml.transform.dom.DOMSource)
|
||||
* @see #unmarshalSaxSource(javax.xml.transform.sax.SAXSource)
|
||||
* @see #unmarshalStreamSource(javax.xml.transform.stream.StreamSource)
|
||||
*/
|
||||
@Override
|
||||
public final Object unmarshal(Source source) throws IOException, XmlMappingException {
|
||||
if (source instanceof DOMSource) {
|
||||
return unmarshalDomSource((DOMSource) source);
|
||||
}
|
||||
else if (StaxUtils.isStaxSource(source)) {
|
||||
return unmarshalStaxSource(source);
|
||||
}
|
||||
else if (source instanceof SAXSource) {
|
||||
return unmarshalSaxSource((SAXSource) source);
|
||||
}
|
||||
else if (source instanceof StreamSource) {
|
||||
return unmarshalStreamSourceNoExternalEntitities((StreamSource) source);
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("Unknown Source type: " + source.getClass());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Template method for handling {@code DOMSource}s.
|
||||
* <p>This implementation delegates to {@code unmarshalDomNode}.
|
||||
@@ -322,19 +335,7 @@ public abstract class AbstractMarshaller implements Marshaller, Unmarshaller {
|
||||
*/
|
||||
protected Object unmarshalDomSource(DOMSource domSource) throws XmlMappingException {
|
||||
if (domSource.getNode() == null) {
|
||||
try {
|
||||
synchronized (this.documentBuilderFactoryMonitor) {
|
||||
if (this.documentBuilderFactory == null) {
|
||||
this.documentBuilderFactory = createDocumentBuilderFactory();
|
||||
}
|
||||
}
|
||||
DocumentBuilder documentBuilder = createDocumentBuilder(this.documentBuilderFactory);
|
||||
domSource.setNode(documentBuilder.newDocument());
|
||||
}
|
||||
catch (ParserConfigurationException ex) {
|
||||
throw new UnmarshallingFailureException(
|
||||
"Could not create document placeholder for DOMSource: " + ex.getMessage(), ex);
|
||||
}
|
||||
domSource.setNode(buildDocument());
|
||||
}
|
||||
return unmarshalDomNode(domSource.getNode());
|
||||
}
|
||||
@@ -391,18 +392,17 @@ public abstract class AbstractMarshaller implements Marshaller, Unmarshaller {
|
||||
* Template method for handling {@code StreamSource}s with protection against
|
||||
* the XML External Entity (XXE) processing vulnerability taking into account
|
||||
* the value of the {@link #setProcessExternalEntities(boolean)} property.
|
||||
* <p>
|
||||
* The default implementation wraps the StreamSource as a SAXSource and delegates
|
||||
* <p>The default implementation wraps the StreamSource as a SAXSource and delegates
|
||||
* to {@link #unmarshalSaxSource(javax.xml.transform.sax.SAXSource)}.
|
||||
*
|
||||
* @param streamSource the {@code StreamSource}
|
||||
* @return the object graph
|
||||
* @throws IOException if an I/O exception occurs
|
||||
* @throws XmlMappingException if the given source cannot be mapped to an object
|
||||
*
|
||||
* @see <a href="https://www.owasp.org/index.php/XML_External_Entity_(XXE)_Processing">XML_External_Entity_(XXE)_Processing</a>
|
||||
*/
|
||||
protected Object unmarshalStreamSourceNoExternalEntitities(StreamSource streamSource) throws XmlMappingException, IOException {
|
||||
protected Object unmarshalStreamSourceNoExternalEntitities(StreamSource streamSource)
|
||||
throws XmlMappingException, IOException {
|
||||
|
||||
InputSource inputSource;
|
||||
if (streamSource.getInputStream() != null) {
|
||||
inputSource = new InputSource(streamSource.getInputStream());
|
||||
@@ -420,10 +420,9 @@ public abstract class AbstractMarshaller implements Marshaller, Unmarshaller {
|
||||
/**
|
||||
* Template method for handling {@code StreamSource}s.
|
||||
* <p>This implementation defers to {@code unmarshalInputStream} or {@code unmarshalReader}.
|
||||
* <p>As of 3.2.8 and 4.0.2 this method is no longer invoked from
|
||||
* <p>As of Spring 3.2.8, this method is no longer invoked from
|
||||
* {@link #unmarshal(javax.xml.transform.Source)}. The method invoked instead is
|
||||
* {@link #unmarshalStreamSourceNoExternalEntitities(javax.xml.transform.stream.StreamSource)}.
|
||||
*
|
||||
* @param streamSource the {@code StreamSource}
|
||||
* @return the object graph
|
||||
* @throws IOException if an I/O exception occurs
|
||||
@@ -507,7 +506,6 @@ public abstract class AbstractMarshaller implements Marshaller, Unmarshaller {
|
||||
protected abstract void marshalWriter(Object graph, Writer writer)
|
||||
throws XmlMappingException, IOException;
|
||||
|
||||
|
||||
/**
|
||||
* Abstract template method for unmarshalling from a given DOM {@code Node}.
|
||||
* @param node the DOM node that contains the objects to be unmarshalled
|
||||
|
||||
@@ -113,10 +113,6 @@ public class XmlBeansMarshaller extends AbstractMarshaller {
|
||||
return this.validating;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getDefaultEncoding() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* This implementation returns true if the given class is an implementation of {@link XmlObject}.
|
||||
@@ -327,14 +323,11 @@ public class XmlBeansMarshaller extends AbstractMarshaller {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* See SPR-7034
|
||||
*/
|
||||
private static class NonClosingInputStream extends InputStream {
|
||||
|
||||
private final WeakReference<InputStream> in;
|
||||
|
||||
private NonClosingInputStream(InputStream in) {
|
||||
public NonClosingInputStream(InputStream in) {
|
||||
this.in = new WeakReference<InputStream>(in);
|
||||
}
|
||||
|
||||
@@ -345,31 +338,31 @@ public class XmlBeansMarshaller extends AbstractMarshaller {
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
InputStream in = getInputStream();
|
||||
return in != null ? in.read() : -1;
|
||||
return (in != null ? in.read() : -1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(byte[] b) throws IOException {
|
||||
InputStream in = getInputStream();
|
||||
return in != null ? in.read(b) : -1;
|
||||
return (in != null ? in.read(b) : -1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(byte[] b, int off, int len) throws IOException {
|
||||
InputStream in = getInputStream();
|
||||
return in != null ? in.read(b, off, len) : -1;
|
||||
return (in != null ? in.read(b, off, len) : -1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long skip(long n) throws IOException {
|
||||
InputStream in = getInputStream();
|
||||
return in != null ? in.skip(n) : 0;
|
||||
return (in != null ? in.skip(n) : 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean markSupported() {
|
||||
InputStream in = getInputStream();
|
||||
return in != null && in.markSupported();
|
||||
return (in != null && in.markSupported());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -391,14 +384,14 @@ public class XmlBeansMarshaller extends AbstractMarshaller {
|
||||
@Override
|
||||
public int available() throws IOException {
|
||||
InputStream in = getInputStream();
|
||||
return in != null ? in.available() : 0;
|
||||
return (in != null ? in.available() : 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
InputStream in = getInputStream();
|
||||
if(in != null) {
|
||||
this.in.clear();
|
||||
if (in != null) {
|
||||
this.in.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -408,7 +401,7 @@ public class XmlBeansMarshaller extends AbstractMarshaller {
|
||||
|
||||
private final WeakReference<Reader> reader;
|
||||
|
||||
private NonClosingReader(Reader reader) {
|
||||
public NonClosingReader(Reader reader) {
|
||||
this.reader = new WeakReference<Reader>(reader);
|
||||
}
|
||||
|
||||
@@ -419,43 +412,43 @@ public class XmlBeansMarshaller extends AbstractMarshaller {
|
||||
@Override
|
||||
public int read(CharBuffer target) throws IOException {
|
||||
Reader rdr = getReader();
|
||||
return rdr != null ? rdr.read(target) : -1;
|
||||
return (rdr != null ? rdr.read(target) : -1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
Reader rdr = getReader();
|
||||
return rdr != null ? rdr.read() : -1;
|
||||
return (rdr != null ? rdr.read() : -1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(char[] cbuf) throws IOException {
|
||||
Reader rdr = getReader();
|
||||
return rdr != null ? rdr.read(cbuf) : -1;
|
||||
return (rdr != null ? rdr.read(cbuf) : -1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(char[] cbuf, int off, int len) throws IOException {
|
||||
Reader rdr = getReader();
|
||||
return rdr != null ? rdr.read(cbuf, off, len) : -1;
|
||||
return (rdr != null ? rdr.read(cbuf, off, len) : -1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long skip(long n) throws IOException {
|
||||
Reader rdr = getReader();
|
||||
return rdr != null ? rdr.skip(n) : 0;
|
||||
return (rdr != null ? rdr.skip(n) : 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean ready() throws IOException {
|
||||
Reader rdr = getReader();
|
||||
return rdr != null && rdr.ready();
|
||||
return (rdr != null && rdr.ready());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean markSupported() {
|
||||
Reader rdr = getReader();
|
||||
return rdr != null && rdr.markSupported();
|
||||
return (rdr != null && rdr.markSupported());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -481,7 +474,6 @@ public class XmlBeansMarshaller extends AbstractMarshaller {
|
||||
this.reader.clear();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user