10 changed files with 636 additions and 4 deletions
@ -0,0 +1,144 @@
@@ -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. |
||||
* |
||||
* <p>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<T> implements HttpMessageConverter<T> { |
||||
|
||||
/** |
||||
* Logger available to subclasses. |
||||
*/ |
||||
protected final Log logger = LogFactory.getLog(getClass()); |
||||
|
||||
private List<MediaType> supportedMediaTypes = Collections.emptyList(); |
||||
|
||||
/** |
||||
* Constructs an {@code AbstractHttpMessageConverter} with no supported media types. |
||||
* |
||||
* @see #setSupportedMediaTypes(List) |
||||
*/ |
||||
protected AbstractHttpMessageConverter() { |
||||
} |
||||
|
||||
/** |
||||
* Constructs an {@code AbstractHttpMessageConverter} with one supported media type. |
||||
*/ |
||||
protected AbstractHttpMessageConverter(MediaType supportedMediaType) { |
||||
setSupportedMediaTypes(Collections.singletonList(supportedMediaType)); |
||||
} |
||||
|
||||
/** |
||||
* Constructs an {@code AbstractHttpMessageConverter} with multiple supported media type. |
||||
*/ |
||||
protected AbstractHttpMessageConverter(MediaType... supportedMediaTypes) { |
||||
setSupportedMediaTypes(Arrays.asList(supportedMediaTypes)); |
||||
} |
||||
|
||||
public List<MediaType> getSupportedMediaTypes() { |
||||
return Collections.unmodifiableList(supportedMediaTypes); |
||||
} |
||||
|
||||
/** |
||||
* Sets the list of {@link MediaType} objects supported by this converter. |
||||
*/ |
||||
public void setSupportedMediaTypes(List<MediaType> supportedMediaTypes) { |
||||
Assert.notEmpty(supportedMediaTypes, "'supportedMediaTypes' must not be empty"); |
||||
this.supportedMediaTypes = new ArrayList<MediaType>(supportedMediaTypes); |
||||
} |
||||
|
||||
/** |
||||
* {@inheritDoc} |
||||
* |
||||
* <p>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. |
||||
* |
||||
* <p>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 <code>null</code> if not known |
||||
*/ |
||||
protected MediaType getContentType(T t) { |
||||
List<MediaType> mediaTypes = getSupportedMediaTypes(); |
||||
return !mediaTypes.isEmpty() ? mediaTypes.get(0) : null; |
||||
} |
||||
|
||||
/** |
||||
* Returns the content length for the given type. |
||||
* |
||||
* <p>By default, this returns <code>null</code>. Can be overriden in subclasses. |
||||
* |
||||
* @param t the type to return the content length for |
||||
* @return the content length, or <code>null</code> 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; |
||||
|
||||
} |
@ -0,0 +1,79 @@
@@ -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. |
||||
* |
||||
* <p>By default, this converter supports all media types (<code>*/*</code>), 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 AbstractHttpMessageConverter<byte[]> { |
||||
|
||||
/** |
||||
* Creates a new instance of the {@code ByteArrayHttpMessageConverter}. |
||||
*/ |
||||
public ByteArrayHttpMessageConverter() { |
||||
super(MediaType.ALL); |
||||
} |
||||
|
||||
public boolean supports(Class<? extends byte[]> clazz) { |
||||
return byte[].class == clazz; |
||||
} |
||||
|
||||
public byte[] read(Class<byte[]> clazz, HttpInputMessage inputMessage) throws IOException { |
||||
long contentLength = inputMessage.getHeaders().getContentLength(); |
||||
if (contentLength >= 0) { |
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream((int) contentLength); |
||||
FileCopyUtils.copy(inputMessage.getBody(), bos); |
||||
return bos.toByteArray(); |
||||
} |
||||
else { |
||||
return FileCopyUtils.copyToByteArray(inputMessage.getBody()); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
protected MediaType getContentType(byte[] bytes) { |
||||
return new MediaType("application", "octet-stream"); |
||||
} |
||||
|
||||
@Override |
||||
protected Long getContentLength(byte[] bytes) { |
||||
return (long) bytes.length; |
||||
} |
||||
|
||||
@Override |
||||
protected void writeToInternal(byte[] bytes, HttpOutputMessage outputMessage) throws IOException { |
||||
FileCopyUtils.copy(bytes, outputMessage.getBody()); |
||||
} |
||||
|
||||
|
||||
} |
@ -0,0 +1,47 @@
@@ -0,0 +1,47 @@
|
||||
/* |
||||
* 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 org.springframework.core.NestedRuntimeException; |
||||
|
||||
/** |
||||
* Thrown by {@link HttpMessageConverter} implementations when the conversion fails. |
||||
* |
||||
* @author Arjen Poutsma |
||||
* @since 3.0 |
||||
*/ |
||||
public class HttpMessageConversionException extends NestedRuntimeException { |
||||
|
||||
/** |
||||
* Create a new MessageConversionException. |
||||
* |
||||
* @param msg the detail message |
||||
*/ |
||||
public HttpMessageConversionException(String msg) { |
||||
super(msg); |
||||
} |
||||
|
||||
/** |
||||
* Create a new MessageConversionException. |
||||
* |
||||
* @param msg the detail message |
||||
* @param cause the root cause (if any) |
||||
*/ |
||||
public HttpMessageConversionException(String msg, Throwable cause) { |
||||
super(msg, cause); |
||||
} |
||||
} |
@ -0,0 +1,70 @@
@@ -0,0 +1,70 @@
|
||||
/* |
||||
* 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.List; |
||||
|
||||
import org.springframework.util.MediaType; |
||||
import org.springframework.web.http.HttpInputMessage; |
||||
import org.springframework.web.http.HttpOutputMessage; |
||||
|
||||
/** |
||||
* Strategy interface that specifies a converter can convert from and to HTTP request and responses. |
||||
* |
||||
* @author Arjen Poutsma |
||||
* @since 3.0 |
||||
*/ |
||||
public interface HttpMessageConverter<T> { |
||||
|
||||
/** |
||||
* Indicates whether the given class is supported by this converter. |
||||
* |
||||
* <p>Typically implemented using an {@code instanceof} check. |
||||
* |
||||
* @param clazz the class to test for support |
||||
* @return <code>true</code> if supported; <code>false</code> otherwise |
||||
*/ |
||||
boolean supports(Class<? extends T> clazz); |
||||
|
||||
/** |
||||
* Returns the list of {@link MediaType} objects supported by this converter. |
||||
*/ |
||||
List<MediaType> getSupportedMediaTypes(); |
||||
|
||||
/** |
||||
* Reads an object of the given type form the given input message, and returns it. |
||||
* |
||||
* @param clazz the type of object to return |
||||
* @param inputMessage the HTTP input message to read from |
||||
* @return the converted object |
||||
* @throws IOException in case of I/O errors |
||||
* @throws HttpMessageConversionException in case of conversion errors |
||||
*/ |
||||
T read(Class<T> clazz, HttpInputMessage inputMessage) throws IOException; |
||||
|
||||
/** |
||||
* Writes an given object to the given output message. |
||||
* |
||||
* @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 |
||||
*/ |
||||
void write(T t, HttpOutputMessage outputMessage) throws IOException; |
||||
|
||||
} |
@ -0,0 +1,99 @@
@@ -0,0 +1,99 @@
|
||||
/* |
||||
* 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.io.InputStreamReader; |
||||
import java.io.OutputStreamWriter; |
||||
import java.io.UnsupportedEncodingException; |
||||
import java.nio.charset.Charset; |
||||
import java.util.ArrayList; |
||||
import java.util.List; |
||||
|
||||
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 strings. |
||||
* |
||||
* <p>By default, this converter supports all text media types (<code>text/*</code>), 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 AbstractHttpMessageConverter<String> { |
||||
|
||||
public static final Charset DEFAULT_CHARSET = Charset.forName("ISO-8859-1"); |
||||
|
||||
private final List<Charset> availableCharsets; |
||||
|
||||
public StringHttpMessageConverter() { |
||||
super(new MediaType("text", "plain", DEFAULT_CHARSET), new MediaType("text", "*")); |
||||
availableCharsets = new ArrayList<Charset>(Charset.availableCharsets().values()); |
||||
} |
||||
|
||||
public boolean supports(Class<? extends String> clazz) { |
||||
return String.class.equals(clazz); |
||||
} |
||||
|
||||
public String read(Class<String> clazz, HttpInputMessage inputMessage) throws IOException { |
||||
MediaType contentType = inputMessage.getHeaders().getContentType(); |
||||
Charset charset = contentType.getCharSet() != null ? contentType.getCharSet() : DEFAULT_CHARSET; |
||||
return FileCopyUtils.copyToString(new InputStreamReader(inputMessage.getBody(), charset)); |
||||
} |
||||
|
||||
@Override |
||||
protected Long getContentLength(String s) { |
||||
Charset charset = getContentType(s).getCharSet(); |
||||
if (charset != null) { |
||||
try { |
||||
return (long) s.getBytes(charset.name()).length; |
||||
} |
||||
catch (UnsupportedEncodingException ex) { |
||||
// should not occur
|
||||
throw new InternalError(ex.getMessage()); |
||||
} |
||||
} |
||||
else { |
||||
return null; |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
protected void writeToInternal(String s, HttpOutputMessage outputMessage) throws IOException { |
||||
outputMessage.getHeaders().setAcceptCharset(getAcceptedCharsets()); |
||||
MediaType contentType = getContentType(s); |
||||
Charset charset = contentType.getCharSet() != null ? contentType.getCharSet() : DEFAULT_CHARSET; |
||||
FileCopyUtils.copy(s, new OutputStreamWriter(outputMessage.getBody(), charset)); |
||||
} |
||||
|
||||
/** |
||||
* Returns the list of supported {@link Charset}. |
||||
* |
||||
* <p>By default, returns {@link Charset#availableCharsets()}. Can be overridden in subclasses. |
||||
* |
||||
* @return the list of accepted charsets |
||||
*/ |
||||
protected List<Charset> getAcceptedCharsets() { |
||||
return availableCharsets; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,7 @@
@@ -0,0 +1,7 @@
|
||||
<html> |
||||
<body> |
||||
|
||||
Provides a HttpMessageConverter abstraction to convert between Java objects and HTTP input/output messages. |
||||
|
||||
</body> |
||||
</html> |
@ -0,0 +1,61 @@
@@ -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()); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,66 @@
@@ -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()); |
||||
} |
||||
} |
Loading…
Reference in new issue