diff --git a/org.springframework.web/src/main/java/org/springframework/http/converter/ResourceHttpMessageConverter.java b/org.springframework.web/src/main/java/org/springframework/http/converter/ResourceHttpMessageConverter.java index a17233a5649..978b9f32e9d 100644 --- a/org.springframework.web/src/main/java/org/springframework/http/converter/ResourceHttpMessageConverter.java +++ b/org.springframework.web/src/main/java/org/springframework/http/converter/ResourceHttpMessageConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2010 the original author or authors. + * Copyright 2002-2011 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. @@ -18,15 +18,12 @@ package org.springframework.http.converter; import java.io.IOException; import java.io.InputStream; -import java.util.Collections; -import java.util.List; import javax.activation.FileTypeMap; import javax.activation.MimetypesFileTypeMap; import org.springframework.core.io.ByteArrayResource; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; -import org.springframework.http.HttpHeaders; import org.springframework.http.HttpInputMessage; import org.springframework.http.HttpOutputMessage; import org.springframework.http.MediaType; @@ -44,49 +41,28 @@ import org.springframework.util.StringUtils; * @author Arjen Poutsma * @since 3.0.2 */ -public class ResourceHttpMessageConverter implements HttpMessageConverter { +public class ResourceHttpMessageConverter extends AbstractHttpMessageConverter { private static final boolean jafPresent = ClassUtils.isPresent("javax.activation.FileTypeMap", ResourceHttpMessageConverter.class.getClassLoader()); - public boolean canRead(Class clazz, MediaType mediaType) { + public ResourceHttpMessageConverter() { + super(MediaType.ALL); + } + @Override + protected boolean supports(Class clazz) { return Resource.class.isAssignableFrom(clazz); } - public boolean canWrite(Class clazz, MediaType mediaType) { - return Resource.class.isAssignableFrom(clazz); - } - - public List getSupportedMediaTypes() { - return Collections.singletonList(MediaType.ALL); - } - - public Resource read(Class clazz, HttpInputMessage inputMessage) + @Override + protected Resource readInternal(Class clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException { - byte[] body = FileCopyUtils.copyToByteArray(inputMessage.getBody()); return new ByteArrayResource(body); } - public void write(Resource resource, MediaType contentType, HttpOutputMessage outputMessage) - throws IOException, HttpMessageNotWritableException { - - HttpHeaders headers = outputMessage.getHeaders(); - if (contentType == null || contentType.isWildcardType() || contentType.isWildcardSubtype()) { - contentType = getContentType(resource); - } - if (contentType != null) { - headers.setContentType(contentType); - } - Long contentLength = getContentLength(resource, contentType); - if (contentLength != null) { - headers.setContentLength(contentLength); - } - FileCopyUtils.copy(resource.getInputStream(), outputMessage.getBody()); - outputMessage.getBody().flush(); - } - - private MediaType getContentType(Resource resource) { + @Override + protected MediaType getDefaultContentType(Resource resource) { if (jafPresent) { return ActivationMediaTypeFactory.getMediaType(resource); } @@ -95,10 +71,22 @@ public class ResourceHttpMessageConverter implements HttpMessageConverter extends AbstractXmlHtt return new ByteArrayInputStream(bos.toByteArray()); } + @Override + protected Long getContentLength(T t, MediaType contentType) { + if (t instanceof DOMSource) { + try { + CountingOutputStream os = new CountingOutputStream(); + transform(t, new StreamResult(os)); + return os.count; + } + catch (TransformerException ex) { + // ignore + } + } + return null; + } + @Override protected void writeToResult(T t, HttpHeaders headers, Result result) throws IOException { try { @@ -94,4 +111,24 @@ public class SourceHttpMessageConverter extends AbstractXmlHtt } } + private static class CountingOutputStream extends OutputStream { + + private long count = 0; + + @Override + public void write(int b) throws IOException { + count++; + } + + @Override + public void write(byte[] b) throws IOException { + count += b.length; + } + + @Override + public void write(byte[] b, int off, int len) throws IOException { + count += len; + } + } + } diff --git a/org.springframework.web/src/test/java/org/springframework/http/converter/xml/SourceHttpMessageConverterTests.java b/org.springframework.web/src/test/java/org/springframework/http/converter/xml/SourceHttpMessageConverterTests.java index bb20f8a2130..3dbbb80e5f2 100644 --- a/org.springframework.web/src/test/java/org/springframework/http/converter/xml/SourceHttpMessageConverterTests.java +++ b/org.springframework.web/src/test/java/org/springframework/http/converter/xml/SourceHttpMessageConverterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2010 the original author or authors. + * Copyright 2002-2011 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. @@ -17,6 +17,7 @@ package org.springframework.http.converter.xml; import java.io.InputStreamReader; +import java.io.StringReader; import java.nio.charset.Charset; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.transform.Source; @@ -24,7 +25,6 @@ import javax.xml.transform.dom.DOMSource; import javax.xml.transform.sax.SAXSource; import javax.xml.transform.stream.StreamSource; -import static org.custommonkey.xmlunit.XMLAssert.*; import org.junit.Before; import org.junit.Test; import org.w3c.dom.Document; @@ -36,6 +36,8 @@ import org.springframework.http.MockHttpInputMessage; import org.springframework.http.MockHttpOutputMessage; import org.springframework.util.FileCopyUtils; +import static org.custommonkey.xmlunit.XMLAssert.*; + /** @author Arjen Poutsma */ @SuppressWarnings("unchecked") public class SourceHttpMessageConverterTests { @@ -100,7 +102,7 @@ public class SourceHttpMessageConverterTests { } @Test - public void write() throws Exception { + public void writeDOMSource() throws Exception { DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); documentBuilderFactory.setNamespaceAware(true); Document document = documentBuilderFactory.newDocumentBuilder().newDocument(); @@ -115,7 +117,34 @@ public class SourceHttpMessageConverterTests { outputMessage.getBodyAsString(Charset.forName("UTF-8"))); assertEquals("Invalid content-type", new MediaType("application", "xml"), outputMessage.getHeaders().getContentType()); + assertEquals("Invalid content-length", outputMessage.getBodyAsBytes().length, + outputMessage.getHeaders().getContentLength()); + } + + @Test + public void writeSAXSource() throws Exception { + String xml = "Hello World"; + SAXSource saxSource = new SAXSource(new InputSource(new StringReader(xml))); + + MockHttpOutputMessage outputMessage = new MockHttpOutputMessage(); + converter.write(saxSource, null, outputMessage); + assertXMLEqual("Invalid result", "Hello World", + outputMessage.getBodyAsString(Charset.forName("UTF-8"))); + assertEquals("Invalid content-type", new MediaType("application", "xml"), + outputMessage.getHeaders().getContentType()); } + @Test + public void writeStreamSource() throws Exception { + String xml = "Hello World"; + StreamSource streamSource = new StreamSource(new StringReader(xml)); + + MockHttpOutputMessage outputMessage = new MockHttpOutputMessage(); + converter.write(streamSource, null, outputMessage); + assertXMLEqual("Invalid result", "Hello World", + outputMessage.getBodyAsString(Charset.forName("UTF-8"))); + assertEquals("Invalid content-type", new MediaType("application", "xml"), + outputMessage.getHeaders().getContentType()); + } }