diff --git a/org.springframework.web/src/main/java/org/springframework/web/converter/AbstractHttpMessageConverter.java b/org.springframework.web/src/main/java/org/springframework/web/converter/AbstractHttpMessageConverter.java new file mode 100644 index 00000000000..b9a08d3284c --- /dev/null +++ b/org.springframework.web/src/main/java/org/springframework/web/converter/AbstractHttpMessageConverter.java @@ -0,0 +1,144 @@ +/* + * Copyright 2002-2009 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.web.converter; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.springframework.util.Assert; +import org.springframework.util.MediaType; +import org.springframework.web.http.HttpHeaders; +import org.springframework.web.http.HttpOutputMessage; + +/** + * Abstract base class for most {@link HttpMessageConverter} implementations. + * + *
This base class adds support for setting supported {@code MediaTypes}, through the {@link
+ * #setSupportedMediaTypes(List) supportedMediaTypes} bean property. It also adds support for {@code Content-Type} and
+ * {@code Content-Length} when writing to output messages.
+ *
+ * @author Arjen Poutsma
+ * @since 3.0
+ */
+public abstract class AbstractHttpMessageConverter This implementation delegates to {@link #getContentType(Object)} and {@link #getContentLength(Object)}, and sets
+ * the corresponding headers on the output message. It then calls {@link #writeToInternal(Object, HttpOutputMessage)}.
+ *
+ * @throws HttpMessageConversionException in case of conversion errors
+ */
+ public final void write(T t, HttpOutputMessage outputMessage) throws IOException {
+ HttpHeaders headers = outputMessage.getHeaders();
+ MediaType contentType = getContentType(t);
+ if (contentType != null) {
+ headers.setContentType(contentType);
+ }
+ Long contentLength = getContentLength(t);
+ if (contentLength != null) {
+ headers.setContentLength(contentLength);
+ }
+ writeToInternal(t, outputMessage);
+ outputMessage.getBody().flush();
+ }
+
+ /**
+ * Returns the content type for the given type.
+ *
+ * By default, this returns the first element of the {@link #setSupportedMediaTypes(List) supportedMediaTypes}
+ * property, if any. Can be overriden in subclasses.
+ *
+ * @param t the type to return the content type for
+ * @return the content type, or By default, this returns By default, this converter supports all media types ( Typically implemented using an {@code instanceof} check.
+ *
+ * @param clazz the class to test for support
+ * @return By default, this converter supports all text media types ( By default, returns {@link Charset#availableCharsets()}. Can be overridden in subclasses.
+ *
+ * @return the list of accepted charsets
+ */
+ protected List Returns an empty list when the acceptable media types are unspecified.
*
* @return the acceptable media types
*/
@@ -70,7 +76,7 @@ public final class HttpHeaders implements Mapnull if not known
+ */
+ protected MediaType getContentType(T t) {
+ Listnull. Can be overriden in subclasses.
+ *
+ * @param t the type to return the content length for
+ * @return the content length, or null if not known
+ */
+ protected Long getContentLength(T t) {
+ return null;
+ }
+
+ /**
+ * Abstract template method that writes the actualy body. Invoked from {@link #write(Object, HttpOutputMessage)}.
+ *
+ * @param t the object to write to the output message
+ * @param outputMessage the message to write to
+ * @throws IOException in case of I/O errors
+ * @throws HttpMessageConversionException in case of conversion errors
+ */
+ protected abstract void writeToInternal(T t, HttpOutputMessage outputMessage) throws IOException;
+
+}
diff --git a/org.springframework.web/src/main/java/org/springframework/web/converter/ByteArrayHttpMessageConverter.java b/org.springframework.web/src/main/java/org/springframework/web/converter/ByteArrayHttpMessageConverter.java
new file mode 100644
index 00000000000..c64455a7bc3
--- /dev/null
+++ b/org.springframework.web/src/main/java/org/springframework/web/converter/ByteArrayHttpMessageConverter.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2002-2009 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.web.converter;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+import org.springframework.util.FileCopyUtils;
+import org.springframework.util.MediaType;
+import org.springframework.web.http.HttpInputMessage;
+import org.springframework.web.http.HttpOutputMessage;
+
+/**
+ * Implementation of {@link HttpMessageConverter} that can read and write byte arrays.
+ *
+ * */*), and writes with a {@code
+ * Content-Type} of {@code application/octet-stream}. This can be overridden by setting the {@link
+ * #setSupportedMediaTypes(java.util.List) supportedMediaTypes} property, and overridding {@link
+ * #getContentType(byte[])}.
+ *
+ * @author Arjen Poutsma
+ * @since 3.0
+ */
+public class ByteArrayHttpMessageConverter extends AbstractHttpMessageConvertertrue if supported; false otherwise
+ */
+ boolean supports(Class extends T> clazz);
+
+ /**
+ * Returns the list of {@link MediaType} objects supported by this converter.
+ */
+ Listtext/*), and writes with a {@code
+ * Content-Type} of {@code text/plain}. This can be overridden by setting the {@link
+ * #setSupportedMediaTypes(java.util.List) supportedMediaTypes} property.
+ *
+ * @author Arjen Poutsma
+ * @since 3.0
+ */
+public class StringHttpMessageConverter extends AbstractHttpMessageConverterAccept header.
- * Returns an empty list when the acceptable media types are unspecified.
+ * Returns the list of acceptable {@linkplain MediaType media types}, as specified by the Accept header.
+ *
+ * Accept header.
+ * Sets the list of acceptable {@linkplain MediaType media types}, as specified by the Accept header.
*
* @param acceptableMediaTypes the acceptable media types
*/
@@ -78,6 +84,47 @@ public final class HttpHeaders implements MapAccept-Charset
+ * header.
+ *
+ * @return the acceptable charsets
+ */
+ public ListAccept-Charset header.
+ *
+ * @param acceptableCharsets the acceptable charsets
+ */
+ public void setAcceptCharset(ListAllow header.
* Returns an empty set when the allowed methods are unspecified.
diff --git a/org.springframework.web/src/test/java/org/springframework/web/converter/ByteArrayHttpMessageConverterTests.java b/org.springframework.web/src/test/java/org/springframework/web/converter/ByteArrayHttpMessageConverterTests.java
new file mode 100644
index 00000000000..274a45f237e
--- /dev/null
+++ b/org.springframework.web/src/test/java/org/springframework/web/converter/ByteArrayHttpMessageConverterTests.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2002-2009 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.web.converter;
+
+import java.io.IOException;
+
+import static org.junit.Assert.*;
+import org.junit.Before;
+import org.junit.Test;
+
+import org.springframework.util.MediaType;
+import org.springframework.web.http.MockHttpInputMessage;
+import org.springframework.web.http.MockHttpOutputMessage;
+
+/**
+ * @author Arjen Poutsma
+ */
+public class ByteArrayHttpMessageConverterTests {
+
+ private ByteArrayHttpMessageConverter converter;
+
+ @Before
+ public void setUp() {
+ converter = new ByteArrayHttpMessageConverter();
+ }
+
+ @Test
+ public void read() throws IOException {
+ byte[] body = new byte[]{0x1, 0x2};
+ MockHttpInputMessage inputMessage = new MockHttpInputMessage(body);
+ inputMessage.getHeaders().setContentType(new MediaType("application", "octet-stream"));
+ byte[] result = converter.read(byte[].class, inputMessage);
+ assertArrayEquals("Invalid result", body, result);
+ }
+
+ @Test
+ public void write() throws IOException {
+ MockHttpOutputMessage outputMessage = new MockHttpOutputMessage();
+ byte[] body = new byte[]{0x1, 0x2};
+ converter.write(body, outputMessage);
+ assertArrayEquals("Invalid result", body, outputMessage.getBodyAsBytes());
+ assertEquals("Invalid content-type", new MediaType("application", "octet-stream"),
+ outputMessage.getHeaders().getContentType());
+ assertEquals("Invalid content-length", 2, outputMessage.getHeaders().getContentLength());
+ }
+
+}
diff --git a/org.springframework.web/src/test/java/org/springframework/web/converter/StringHttpMessageConverterTests.java b/org.springframework.web/src/test/java/org/springframework/web/converter/StringHttpMessageConverterTests.java
new file mode 100644
index 00000000000..af6dd9328f3
--- /dev/null
+++ b/org.springframework.web/src/test/java/org/springframework/web/converter/StringHttpMessageConverterTests.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2002-2009 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.web.converter;
+
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.util.Collections;
+
+import static org.junit.Assert.*;
+import org.junit.Before;
+import org.junit.Test;
+
+import org.springframework.util.MediaType;
+import org.springframework.web.http.MockHttpInputMessage;
+import org.springframework.web.http.MockHttpOutputMessage;
+
+/**
+ * @author Arjen Poutsma
+ */
+public class StringHttpMessageConverterTests {
+
+ private StringHttpMessageConverter converter;
+
+ @Before
+ public void setUp() {
+ converter = new StringHttpMessageConverter();
+ }
+
+ @Test
+ public void read() throws IOException {
+ String body = "Hello World";
+ Charset charset = Charset.forName("UTF-8");
+ MockHttpInputMessage inputMessage = new MockHttpInputMessage(body.getBytes(charset));
+ inputMessage.getHeaders().setContentType(new MediaType("text", "plain", charset));
+ String result = converter.read(String.class, inputMessage);
+ assertEquals("Invalid result", body, result);
+ }
+
+ @Test
+ public void write() throws IOException {
+ Charset charset = Charset.forName("UTF-8");
+ converter.setSupportedMediaTypes(Collections.singletonList(new MediaType("text", "plain", charset)));
+ MockHttpOutputMessage outputMessage = new MockHttpOutputMessage();
+ String body = "HŽllo W¿rld";
+ converter.write(body, outputMessage);
+ assertEquals("Invalid result", body, outputMessage.getBodyAsString(charset));
+ assertEquals("Invalid content-type", new MediaType("text", "plain", charset),
+ outputMessage.getHeaders().getContentType());
+ assertEquals("Invalid content-length", 13, outputMessage.getHeaders().getContentLength());
+ assertFalse("Invalid accept-charset", outputMessage.getHeaders().getAcceptCharset().isEmpty());
+ }
+}
diff --git a/org.springframework.web/src/test/java/org/springframework/web/http/HttpHeadersTests.java b/org.springframework.web/src/test/java/org/springframework/web/http/HttpHeadersTests.java
index 9d2bc4e1eae..e4ec4a82880 100644
--- a/org.springframework.web/src/test/java/org/springframework/web/http/HttpHeadersTests.java
+++ b/org.springframework.web/src/test/java/org/springframework/web/http/HttpHeadersTests.java
@@ -50,7 +50,19 @@ public class HttpHeadersTests {
mediaTypes.add(mediaType2);
headers.setAccept(mediaTypes);
assertEquals("Invalid Accept header", mediaTypes, headers.getAccept());
- assertEquals("Invalid Accept header", "text/html,text/plain", headers.getFirst("Accept"));
+ assertEquals("Invalid Accept header", "text/html, text/plain", headers.getFirst("Accept"));
+ }
+
+ @Test
+ public void acceptCharsets() {
+ Charset charset1 = Charset.forName("UTF-8");
+ Charset charset2 = Charset.forName("ISO-8859-1");
+ List