11/*
2- * Copyright 2002-2011 the original author or authors.
2+ * Copyright 2002-2013 the original author or authors.
33 *
44 * Licensed under the Apache License, Version 2.0 (the "License");
55 * you may not use this file except in compliance with the License.
1919import java .io .ByteArrayInputStream ;
2020import java .io .ByteArrayOutputStream ;
2121import java .io .IOException ;
22+ import java .io .InputStream ;
2223import java .io .OutputStream ;
24+ import javax .xml .parsers .DocumentBuilder ;
25+ import javax .xml .parsers .DocumentBuilderFactory ;
26+ import javax .xml .parsers .ParserConfigurationException ;
27+ import javax .xml .stream .XMLInputFactory ;
28+ import javax .xml .stream .XMLStreamException ;
29+ import javax .xml .stream .XMLStreamReader ;
2330import javax .xml .transform .Result ;
2431import javax .xml .transform .Source ;
2532import javax .xml .transform .TransformerException ;
33+ import javax .xml .transform .TransformerFactory ;
2634import javax .xml .transform .dom .DOMResult ;
2735import javax .xml .transform .dom .DOMSource ;
2836import javax .xml .transform .sax .SAXSource ;
2937import javax .xml .transform .stream .StreamResult ;
3038import javax .xml .transform .stream .StreamSource ;
3139
40+ import org .w3c .dom .Document ;
3241import org .xml .sax .InputSource ;
42+ import org .xml .sax .SAXException ;
43+ import org .xml .sax .XMLReader ;
44+ import org .xml .sax .helpers .XMLReaderFactory ;
3345
3446import org .springframework .http .HttpHeaders ;
47+ import org .springframework .http .HttpInputMessage ;
48+ import org .springframework .http .HttpOutputMessage ;
3549import org .springframework .http .MediaType ;
50+ import org .springframework .http .converter .AbstractHttpMessageConverter ;
3651import org .springframework .http .converter .HttpMessageConversionException ;
3752import org .springframework .http .converter .HttpMessageNotReadableException ;
3853import org .springframework .http .converter .HttpMessageNotWritableException ;
54+ import org .springframework .util .StreamUtils ;
55+ import org .springframework .util .xml .StaxUtils ;
3956
4057/**
41- * Implementation of {@link org.springframework.http.converter.HttpMessageConverter} that can read and write {@link
42- * Source} objects.
58+ * Implementation of {@link org.springframework.http.converter.HttpMessageConverter}
59+ * that can read and write {@link Source} objects.
4360 *
4461 * @author Arjen Poutsma
4562 * @since 3.0
4663 */
47- public class SourceHttpMessageConverter <T extends Source > extends AbstractXmlHttpMessageConverter <T > {
64+ public class SourceHttpMessageConverter <T extends Source > extends AbstractHttpMessageConverter <T > {
4865
49- @ Override
50- public boolean supports (Class <?> clazz ) {
51- return DOMSource .class .equals (clazz ) || SAXSource .class .equals (clazz ) || StreamSource .class .equals (clazz ) ||
52- Source .class .equals (clazz );
53- }
66+ private final TransformerFactory transformerFactory = TransformerFactory .newInstance ();
5467
55- @ Override
56- @ SuppressWarnings ("unchecked" )
57- protected T readFromSource (Class clazz , HttpHeaders headers , Source source ) throws IOException {
58- try {
59- if (DOMSource .class .equals (clazz )) {
60- DOMResult domResult = new DOMResult ();
61- transform (source , domResult );
62- return (T ) new DOMSource (domResult .getNode ());
63- }
64- else if (SAXSource .class .equals (clazz )) {
65- ByteArrayInputStream bis = transformToByteArrayInputStream (source );
66- return (T ) new SAXSource (new InputSource (bis ));
67- }
68- else if (StreamSource .class .equals (clazz ) || Source .class .equals (clazz )) {
69- ByteArrayInputStream bis = transformToByteArrayInputStream (source );
70- return (T ) new StreamSource (bis );
71- }
72- else {
73- throw new HttpMessageConversionException ("Could not read class [" + clazz +
74- "]. Only DOMSource, SAXSource, and StreamSource are supported." );
75- }
76- }
77- catch (TransformerException ex ) {
78- throw new HttpMessageNotReadableException ("Could not transform from [" + source + "] to [" + clazz + "]" ,
79- ex );
80- }
81- }
68+ private boolean processExternalEntities = false ;
69+
70+ /**
71+ * Sets the {@link #setSupportedMediaTypes(java.util.List) supportedMediaTypes}
72+ * to {@code text/xml} and {@code application/xml}, and {@code application/*-xml}.
73+ */
74+ public SourceHttpMessageConverter () {
75+ super (MediaType .APPLICATION_XML , MediaType .TEXT_XML , new MediaType ("application" , "*+xml" ));
76+ }
77+
78+
79+ /**
80+ * Indicates whether external XML entities are processed when converting
81+ * to a Source.
82+ * <p>Default is {@code false}, meaning that external entities are not resolved.
83+ */
84+ public void setProcessExternalEntities (boolean processExternalEntities ) {
85+ this .processExternalEntities = processExternalEntities ;
86+ }
8287
83- private ByteArrayInputStream transformToByteArrayInputStream ( Source source ) throws TransformerException {
84- ByteArrayOutputStream bos = new ByteArrayOutputStream ();
85- transform ( source , new StreamResult ( bos ));
86- return new ByteArrayInputStream ( bos . toByteArray () );
88+ @ Override
89+ public boolean supports ( Class <?> clazz ) {
90+ return DOMSource . class . equals ( clazz ) || SAXSource . class . equals ( clazz )
91+ || StreamSource . class . equals ( clazz ) || Source . class . equals ( clazz );
8792 }
8893
94+ @ Override
95+ protected T readInternal (Class <? extends T > clazz , HttpInputMessage inputMessage )
96+ throws IOException , HttpMessageNotReadableException {
97+
98+ InputStream body = inputMessage .getBody ();
99+ if (DOMSource .class .equals (clazz )) {
100+ return (T ) readDOMSource (body );
101+ }
102+ else if (StaxUtils .isStaxSourceClass (clazz )) {
103+ return (T ) readStAXSource (body );
104+ }
105+ else if (SAXSource .class .equals (clazz )) {
106+ return (T ) readSAXSource (body );
107+ }
108+ else if (StreamSource .class .equals (clazz ) || Source .class .equals (clazz )) {
109+ return (T ) readStreamSource (body );
110+ }
111+ else {
112+ throw new HttpMessageConversionException ("Could not read class [" + clazz +
113+ "]. Only DOMSource, SAXSource, and StreamSource are supported." );
114+ }
115+ }
116+
117+ private DOMSource readDOMSource (InputStream body ) throws IOException {
118+ try {
119+ DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory .newInstance ();
120+ documentBuilderFactory .setNamespaceAware (true );
121+ documentBuilderFactory .setFeature ("http://xml.org/sax/features/external-general-entities" , processExternalEntities );
122+ DocumentBuilder documentBuilder = documentBuilderFactory .newDocumentBuilder ();
123+ Document document = documentBuilder .parse (body );
124+ return new DOMSource (document );
125+ }
126+ catch (ParserConfigurationException ex ) {
127+ throw new HttpMessageNotReadableException ("Could not set feature: " + ex .getMessage (), ex );
128+ }
129+ catch (SAXException ex ) {
130+ throw new HttpMessageNotReadableException ("Could not parse document: " + ex .getMessage (), ex );
131+ }
132+ }
133+
134+ private SAXSource readSAXSource (InputStream body ) throws IOException {
135+ try {
136+ XMLReader reader = XMLReaderFactory .createXMLReader ();
137+ reader .setFeature ("http://xml.org/sax/features/external-general-entities" , processExternalEntities );
138+ byte [] bytes = StreamUtils .copyToByteArray (body );
139+ return new SAXSource (reader , new InputSource (new ByteArrayInputStream (bytes )));
140+ }
141+ catch (SAXException ex ) {
142+ throw new HttpMessageNotReadableException ("Could not parse document: " + ex .getMessage (), ex );
143+ }
144+ }
145+
146+ private Source readStAXSource (InputStream body ) {
147+ try {
148+ XMLInputFactory inputFactory = XMLInputFactory .newFactory ();
149+ inputFactory .setProperty ("javax.xml.stream.isSupportingExternalEntities" , processExternalEntities );
150+ XMLStreamReader streamReader = inputFactory .createXMLStreamReader (body );
151+ return StaxUtils .createStaxSource (streamReader );
152+ }
153+ catch (XMLStreamException ex ) {
154+ throw new HttpMessageNotReadableException ("Could not parse document: " + ex .getMessage (), ex );
155+ }
156+ }
157+
158+ private StreamSource readStreamSource (InputStream body ) throws IOException {
159+ byte [] bytes = StreamUtils .copyToByteArray (body );
160+ return new StreamSource (new ByteArrayInputStream (bytes ));
161+ }
162+
89163 @ Override
90164 protected Long getContentLength (T t , MediaType contentType ) {
91165 if (t instanceof DOMSource ) {
@@ -101,17 +175,24 @@ protected Long getContentLength(T t, MediaType contentType) {
101175 return null ;
102176 }
103177
104- @ Override
105- protected void writeToResult (T t , HttpHeaders headers , Result result ) throws IOException {
178+ @ Override
179+ protected void writeInternal (T t , HttpOutputMessage outputMessage )
180+ throws IOException , HttpMessageNotWritableException {
106181 try {
182+ Result result = new StreamResult (outputMessage .getBody ());
107183 transform (t , result );
108184 }
109185 catch (TransformerException ex ) {
110- throw new HttpMessageNotWritableException ("Could not transform [" + t + "] to [" + result + "] " , ex );
186+ throw new HttpMessageNotWritableException ("Could not transform [" + t + "] to output message " , ex );
111187 }
112188 }
113189
114- private static class CountingOutputStream extends OutputStream {
190+ private void transform (Source source , Result result ) throws TransformerException {
191+ this .transformerFactory .newTransformer ().transform (source , result );
192+ }
193+
194+
195+ private static class CountingOutputStream extends OutputStream {
115196
116197 private long count = 0 ;
117198
0 commit comments