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.
1717package org .springframework .http .converter .xml ;
1818
1919import java .io .ByteArrayInputStream ;
20- import java .io .ByteArrayOutputStream ;
2120import java .io .IOException ;
21+ import java .io .InputStream ;
2222import java .io .OutputStream ;
23+ import javax .xml .parsers .DocumentBuilder ;
24+ import javax .xml .parsers .DocumentBuilderFactory ;
25+ import javax .xml .parsers .ParserConfigurationException ;
26+ import javax .xml .stream .XMLInputFactory ;
27+ import javax .xml .stream .XMLStreamException ;
28+ import javax .xml .stream .XMLStreamReader ;
2329import javax .xml .transform .Result ;
2430import javax .xml .transform .Source ;
2531import javax .xml .transform .TransformerException ;
26- import javax .xml .transform .dom . DOMResult ;
32+ import javax .xml .transform .TransformerFactory ;
2733import javax .xml .transform .dom .DOMSource ;
2834import javax .xml .transform .sax .SAXSource ;
35+ import javax .xml .transform .stax .StAXSource ;
2936import javax .xml .transform .stream .StreamResult ;
3037import javax .xml .transform .stream .StreamSource ;
3138
39+ import org .w3c .dom .Document ;
3240import org .xml .sax .InputSource ;
41+ import org .xml .sax .SAXException ;
42+ import org .xml .sax .XMLReader ;
43+ import org .xml .sax .helpers .XMLReaderFactory ;
3344
34- import org .springframework .http .HttpHeaders ;
45+ import org .springframework .http .HttpInputMessage ;
46+ import org .springframework .http .HttpOutputMessage ;
3547import org .springframework .http .MediaType ;
48+ import org .springframework .http .converter .AbstractHttpMessageConverter ;
3649import org .springframework .http .converter .HttpMessageConversionException ;
3750import org .springframework .http .converter .HttpMessageNotReadableException ;
3851import org .springframework .http .converter .HttpMessageNotWritableException ;
52+ import org .springframework .util .StreamUtils ;
3953
4054/**
41- * Implementation of {@link org.springframework.http.converter.HttpMessageConverter} that can read and write {@link
42- * Source} objects.
55+ * Implementation of {@link org.springframework.http.converter.HttpMessageConverter}
56+ * that can read and write {@link Source} objects.
4357 *
4458 * @author Arjen Poutsma
4559 * @since 3.0
4660 */
47- public class SourceHttpMessageConverter <T extends Source > extends AbstractXmlHttpMessageConverter <T > {
61+ public class SourceHttpMessageConverter <T extends Source > extends AbstractHttpMessageConverter <T > {
62+
63+ private final TransformerFactory transformerFactory = TransformerFactory .newInstance ();
64+
65+ private boolean processExternalEntities = false ;
66+
67+ /**
68+ * Sets the {@link #setSupportedMediaTypes(java.util.List) supportedMediaTypes}
69+ * to {@code text/xml} and {@code application/xml}, and {@code application/*-xml}.
70+ */
71+ public SourceHttpMessageConverter () {
72+ super (MediaType .APPLICATION_XML , MediaType .TEXT_XML , new MediaType ("application" , "*+xml" ));
73+ }
74+
75+
76+ /**
77+ * Indicates whether external XML entities are processed when converting
78+ * to a Source.
79+ * <p>Default is {@code false}, meaning that external entities are not resolved.
80+ */
81+ public void setProcessExternalEntities (boolean processExternalEntities ) {
82+ this .processExternalEntities = processExternalEntities ;
83+ }
4884
4985 @ Override
5086 public boolean supports (Class <?> clazz ) {
51- return DOMSource .class .equals (clazz ) || SAXSource .class .equals (clazz ) || StreamSource . class . equals ( clazz ) ||
52- Source .class .equals (clazz );
87+ return DOMSource .class .equals (clazz ) || SAXSource .class .equals (clazz )
88+ || StreamSource . class . equals ( clazz ) || Source .class .equals (clazz );
5389 }
5490
5591 @ Override
56- @ SuppressWarnings ("unchecked" )
57- protected T readFromSource (Class clazz , HttpHeaders headers , Source source ) throws IOException {
92+ protected T readInternal (Class <? extends T > clazz , HttpInputMessage inputMessage )
93+ throws IOException , HttpMessageNotReadableException {
94+
95+ InputStream body = inputMessage .getBody ();
96+ if (DOMSource .class .equals (clazz )) {
97+ return (T ) readDOMSource (body );
98+ }
99+ else if (SAXSource .class .equals (clazz )) {
100+ return (T ) readSAXSource (body );
101+ }
102+ else if (StAXSource .class .equals (clazz )) {
103+ return (T ) readStAXSource (body );
104+ }
105+ else if (StreamSource .class .equals (clazz ) || Source .class .equals (clazz )) {
106+ return (T ) readStreamSource (body );
107+ }
108+ else {
109+ throw new HttpMessageConversionException ("Could not read class [" + clazz +
110+ "]. Only DOMSource, SAXSource, and StreamSource are supported." );
111+ }
112+ }
113+
114+ private DOMSource readDOMSource (InputStream body ) throws IOException {
58115 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- }
116+ DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory .newInstance ();
117+ documentBuilderFactory .setNamespaceAware (true );
118+ documentBuilderFactory .setFeature ("http://xml.org/sax/features/external-general-entities" , processExternalEntities );
119+ DocumentBuilder documentBuilder = documentBuilderFactory .newDocumentBuilder ();
120+ Document document = documentBuilder .parse (body );
121+ return new DOMSource (document );
76122 }
77- catch (TransformerException ex ) {
78- throw new HttpMessageNotReadableException ("Could not transform from [" + source + "] to [" + clazz + "]" ,
79- ex );
123+ catch (ParserConfigurationException ex ) {
124+ throw new HttpMessageNotReadableException ("Could not set feature: " + ex .getMessage (), ex );
125+ }
126+ catch (SAXException ex ) {
127+ throw new HttpMessageNotReadableException ("Could not parse document: " + ex .getMessage (), ex );
80128 }
81129 }
82130
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 ());
131+ private SAXSource readSAXSource (InputStream body ) throws IOException {
132+ try {
133+ XMLReader reader = XMLReaderFactory .createXMLReader ();
134+ reader .setFeature ("http://xml.org/sax/features/external-general-entities" , processExternalEntities );
135+ byte [] bytes = StreamUtils .copyToByteArray (body );
136+ return new SAXSource (reader , new InputSource (new ByteArrayInputStream (bytes )));
137+ }
138+ catch (SAXException ex ) {
139+ throw new HttpMessageNotReadableException ("Could not parse document: " + ex .getMessage (), ex );
140+ }
141+ }
142+
143+ private Source readStAXSource (InputStream body ) {
144+ try {
145+ XMLInputFactory inputFactory = XMLInputFactory .newFactory ();
146+ inputFactory .setProperty ("javax.xml.stream.isSupportingExternalEntities" , processExternalEntities );
147+ XMLStreamReader streamReader = inputFactory .createXMLStreamReader (body );
148+ return new StAXSource (streamReader );
149+ }
150+ catch (XMLStreamException ex ) {
151+ throw new HttpMessageNotReadableException ("Could not parse document: " + ex .getMessage (), ex );
152+ }
153+ }
154+
155+ private StreamSource readStreamSource (InputStream body ) throws IOException {
156+ byte [] bytes = StreamUtils .copyToByteArray (body );
157+ return new StreamSource (new ByteArrayInputStream (bytes ));
87158 }
88159
89160 @ Override
@@ -102,15 +173,22 @@ protected Long getContentLength(T t, MediaType contentType) {
102173 }
103174
104175 @ Override
105- protected void writeToResult (T t , HttpHeaders headers , Result result ) throws IOException {
176+ protected void writeInternal (T t , HttpOutputMessage outputMessage )
177+ throws IOException , HttpMessageNotWritableException {
106178 try {
179+ Result result = new StreamResult (outputMessage .getBody ());
107180 transform (t , result );
108181 }
109182 catch (TransformerException ex ) {
110- throw new HttpMessageNotWritableException ("Could not transform [" + t + "] to [" + result + "] " , ex );
183+ throw new HttpMessageNotWritableException ("Could not transform [" + t + "] to output message " , ex );
111184 }
112185 }
113186
187+ private void transform (Source source , Result result ) throws TransformerException {
188+ this .transformerFactory .newTransformer ().transform (source , result );
189+ }
190+
191+
114192 private static class CountingOutputStream extends OutputStream {
115193
116194 private long count = 0 ;
0 commit comments