1
1
/*
2
- * Copyright 2002-2011 the original author or authors.
2
+ * Copyright 2002-2013 the original author or authors.
3
3
*
4
4
* Licensed under the Apache License, Version 2.0 (the "License");
5
5
* you may not use this file except in compliance with the License.
19
19
import java .io .ByteArrayInputStream ;
20
20
import java .io .ByteArrayOutputStream ;
21
21
import java .io .IOException ;
22
+ import java .io .InputStream ;
22
23
import 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 ;
23
30
import javax .xml .transform .Result ;
24
31
import javax .xml .transform .Source ;
25
32
import javax .xml .transform .TransformerException ;
33
+ import javax .xml .transform .TransformerFactory ;
26
34
import javax .xml .transform .dom .DOMResult ;
27
35
import javax .xml .transform .dom .DOMSource ;
28
36
import javax .xml .transform .sax .SAXSource ;
29
37
import javax .xml .transform .stream .StreamResult ;
30
38
import javax .xml .transform .stream .StreamSource ;
31
39
40
+ import org .w3c .dom .Document ;
32
41
import org .xml .sax .InputSource ;
42
+ import org .xml .sax .SAXException ;
43
+ import org .xml .sax .XMLReader ;
44
+ import org .xml .sax .helpers .XMLReaderFactory ;
33
45
34
46
import org .springframework .http .HttpHeaders ;
47
+ import org .springframework .http .HttpInputMessage ;
48
+ import org .springframework .http .HttpOutputMessage ;
35
49
import org .springframework .http .MediaType ;
50
+ import org .springframework .http .converter .AbstractHttpMessageConverter ;
36
51
import org .springframework .http .converter .HttpMessageConversionException ;
37
52
import org .springframework .http .converter .HttpMessageNotReadableException ;
38
53
import org .springframework .http .converter .HttpMessageNotWritableException ;
54
+ import org .springframework .util .StreamUtils ;
55
+ import org .springframework .util .xml .StaxUtils ;
39
56
40
57
/**
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.
43
60
*
44
61
* @author Arjen Poutsma
45
62
* @since 3.0
46
63
*/
47
- public class SourceHttpMessageConverter <T extends Source > extends AbstractXmlHttpMessageConverter <T > {
64
+ public class SourceHttpMessageConverter <T extends Source > extends AbstractHttpMessageConverter <T > {
48
65
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 ();
54
67
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
+ }
82
87
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 );
87
92
}
88
93
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
+
89
163
@ Override
90
164
protected Long getContentLength (T t , MediaType contentType ) {
91
165
if (t instanceof DOMSource ) {
@@ -101,17 +175,24 @@ protected Long getContentLength(T t, MediaType contentType) {
101
175
return null ;
102
176
}
103
177
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 {
106
181
try {
182
+ Result result = new StreamResult (outputMessage .getBody ());
107
183
transform (t , result );
108
184
}
109
185
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 );
111
187
}
112
188
}
113
189
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 {
115
196
116
197
private long count = 0 ;
117
198
0 commit comments