Browse Source
The MessageConverter interface in spring-messaging is now explicitly designed to support conversion of the payload of a Message<?> to and from serialized form based on MIME type message header. By default, the MessageHeaders.CONTENT_HEADER header is used but a custom ContentTypeResolver can be configured to customize that. Currently available are Jackson 2, String, and byte array converters. A CompositeMessageConverter can be used to configure several message converters in various places such as a messaging template.pull/382/head
34 changed files with 1522 additions and 176 deletions
@ -0,0 +1,206 @@
@@ -0,0 +1,206 @@
|
||||
/* |
||||
* Copyright 2002-2013 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.messaging.support.converter; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.Collection; |
||||
import java.util.Collections; |
||||
import java.util.List; |
||||
|
||||
import org.apache.commons.logging.Log; |
||||
import org.apache.commons.logging.LogFactory; |
||||
import org.springframework.messaging.Message; |
||||
import org.springframework.messaging.MessageHeaders; |
||||
import org.springframework.messaging.support.MessageBuilder; |
||||
import org.springframework.util.Assert; |
||||
import org.springframework.util.MimeType; |
||||
|
||||
|
||||
/** |
||||
* Abstract base class for {@link MessageConverter} implementations including support for |
||||
* common properties and a partial implementation of the conversion methods mainly to |
||||
* check if the converter supports the conversion based on the payload class and MIME |
||||
* type. |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
* @since 4.0 |
||||
*/ |
||||
public abstract class AbstractMessageConverter implements MessageConverter { |
||||
|
||||
protected final Log logger = LogFactory.getLog(getClass()); |
||||
|
||||
private final List<MimeType> supportedMimeTypes; |
||||
|
||||
private Class<?> serializedPayloadClass = byte[].class; |
||||
|
||||
private ContentTypeResolver contentTypeResolver; |
||||
|
||||
|
||||
/** |
||||
* Construct an {@code AbstractMessageConverter} with one supported MIME type. |
||||
* @param supportedMimeType the supported MIME type |
||||
*/ |
||||
protected AbstractMessageConverter(MimeType supportedMimeType) { |
||||
this.supportedMimeTypes = Collections.<MimeType>singletonList(supportedMimeType); |
||||
} |
||||
|
||||
/** |
||||
* Construct an {@code AbstractMessageConverter} with multiple supported MIME type. |
||||
* @param supportedMimeTypes the supported MIME types |
||||
*/ |
||||
protected AbstractMessageConverter(Collection<MimeType> supportedMimeTypes) { |
||||
Assert.notNull(supportedMimeTypes, "'supportedMimeTypes' is required"); |
||||
this.supportedMimeTypes = new ArrayList<MimeType>(supportedMimeTypes); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Return the configured supported MIME types. |
||||
*/ |
||||
public List<MimeType> getSupportedMimeTypes() { |
||||
return Collections.unmodifiableList(this.supportedMimeTypes); |
||||
} |
||||
|
||||
/** |
||||
* Configure the {@link ContentTypeResolver} to use. |
||||
* <p> |
||||
* The default value is {@code null}. However when {@link CompositeMessageConverter} |
||||
* is used it configures all of its delegates with a default resolver. |
||||
*/ |
||||
public void setContentTypeResolver(ContentTypeResolver resolver) { |
||||
this.contentTypeResolver = resolver; |
||||
} |
||||
|
||||
/** |
||||
* Return the default {@link ContentTypeResolver}. |
||||
*/ |
||||
public ContentTypeResolver getContentTypeResolver() { |
||||
return this.contentTypeResolver; |
||||
} |
||||
|
||||
/** |
||||
* Configure the preferred serialization class to use (byte[] or String) when |
||||
* converting an Object payload to a {@link Message}. |
||||
* <p> |
||||
* The default value is byte[]. |
||||
* |
||||
* @param clazz either byte[] or String |
||||
*/ |
||||
public void setSerializedPayloadClass(Class<?> clazz) { |
||||
Assert.isTrue(byte[].class.equals(clazz) || String.class.equals(clazz), |
||||
"Payload class must be byte[] or String: " + clazz); |
||||
this.serializedPayloadClass = clazz; |
||||
} |
||||
|
||||
/** |
||||
* Return the configured preferred serialization payload class. |
||||
*/ |
||||
public Class<?> getSerializedPayloadClass() { |
||||
return this.serializedPayloadClass; |
||||
} |
||||
|
||||
/** |
||||
* Returns the default content type for the payload. Called when |
||||
* {@link #toMessage(Object, MessageHeaders)} is invoked without message headers or |
||||
* without a content type header. |
||||
* <p> |
||||
* By default, this returns the first element of the {@link #getSupportedMimeTypes() |
||||
* supportedMimeTypes}, if any. Can be overridden in sub-classes. |
||||
* |
||||
* @param payload the payload being converted to message |
||||
* @return the content type, or {@code null} if not known |
||||
*/ |
||||
protected MimeType getDefaultContentType(Object payload) { |
||||
List<MimeType> mimeTypes = getSupportedMimeTypes(); |
||||
return (!mimeTypes.isEmpty() ? mimeTypes.get(0) : null); |
||||
} |
||||
|
||||
/** |
||||
* Whether the given class is supported by this converter. |
||||
* |
||||
* @param clazz the class to test for support |
||||
* @return {@code true} if supported; {@code false} otherwise |
||||
*/ |
||||
protected abstract boolean supports(Class<?> clazz); |
||||
|
||||
|
||||
@Override |
||||
public final Object fromMessage(Message<?> message, Class<?> targetClass) { |
||||
if (!canConvertFrom(message, targetClass)) { |
||||
return null; |
||||
} |
||||
return convertFromInternal(message, targetClass); |
||||
} |
||||
|
||||
protected boolean canConvertFrom(Message<?> message, Class<?> targetClass) { |
||||
return (supports(targetClass) && supportsMimeType(message.getHeaders())); |
||||
} |
||||
|
||||
/** |
||||
* Convert the message payload from serialized form to an Object. |
||||
*/ |
||||
public abstract Object convertFromInternal(Message<?> message, Class<?> targetClass); |
||||
|
||||
@Override |
||||
public final Message<?> toMessage(Object payload, MessageHeaders headers) { |
||||
if (!canConvertTo(payload, headers)) { |
||||
return null; |
||||
} |
||||
payload = convertToInternal(payload, headers); |
||||
MessageBuilder<?> builder = MessageBuilder.withPayload(payload); |
||||
if (headers != null) { |
||||
builder.copyHeaders(headers); |
||||
} |
||||
MimeType mimeType = getDefaultContentType(payload); |
||||
if (mimeType != null) { |
||||
builder.setHeaderIfAbsent(MessageHeaders.CONTENT_TYPE, mimeType); |
||||
} |
||||
return builder.build(); |
||||
} |
||||
|
||||
protected boolean canConvertTo(Object payload, MessageHeaders headers) { |
||||
Class<?> clazz = (payload != null) ? payload.getClass() : null; |
||||
return (supports(clazz) && supportsMimeType(headers)); |
||||
} |
||||
|
||||
/** |
||||
* Convert the payload object to serialized form. |
||||
*/ |
||||
public abstract Object convertToInternal(Object payload, MessageHeaders headers); |
||||
|
||||
protected boolean supportsMimeType(MessageHeaders headers) { |
||||
MimeType mimeType = getMimeType(headers); |
||||
if (mimeType == null) { |
||||
return true; |
||||
} |
||||
if (getSupportedMimeTypes().isEmpty()) { |
||||
return true; |
||||
} |
||||
for (MimeType supported : getSupportedMimeTypes()) { |
||||
if (supported.getType().equals(mimeType.getType()) && |
||||
supported.getSubtype().equals(mimeType.getSubtype())) { |
||||
return true; |
||||
} |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
protected MimeType getMimeType(MessageHeaders headers) { |
||||
return (this.contentTypeResolver != null) ? this.contentTypeResolver.resolve(headers) : null; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,54 @@
@@ -0,0 +1,54 @@
|
||||
/* |
||||
* Copyright 2002-2013 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.messaging.support.converter; |
||||
|
||||
import org.springframework.messaging.Message; |
||||
import org.springframework.messaging.MessageHeaders; |
||||
import org.springframework.util.MimeTypeUtils; |
||||
|
||||
|
||||
/** |
||||
* A {@link MessageConverter} that supports MIME type "application/octet-stream" with the |
||||
* payload converted to and from a byte[]. |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
* @since 4.0 |
||||
*/ |
||||
public class ByteArrayMessageConverter extends AbstractMessageConverter { |
||||
|
||||
|
||||
public ByteArrayMessageConverter() { |
||||
super(MimeTypeUtils.APPLICATION_OCTET_STREAM); |
||||
} |
||||
|
||||
|
||||
@Override |
||||
protected boolean supports(Class<?> clazz) { |
||||
return byte[].class.equals(clazz); |
||||
} |
||||
|
||||
@Override |
||||
public Object convertFromInternal(Message<?> message, Class<?> targetClass) { |
||||
return message.getPayload(); |
||||
} |
||||
|
||||
@Override |
||||
public Object convertToInternal(Object payload, MessageHeaders headers) { |
||||
return payload; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,110 @@
@@ -0,0 +1,110 @@
|
||||
/* |
||||
* Copyright 2002-2013 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.messaging.support.converter; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.Collection; |
||||
import java.util.List; |
||||
|
||||
import org.springframework.messaging.Message; |
||||
import org.springframework.messaging.MessageHeaders; |
||||
import org.springframework.util.Assert; |
||||
|
||||
|
||||
/** |
||||
* A {@link MessageConverter} that delegates to a list of other converters to invoke until |
||||
* one of them returns a non-null value. |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
* @since 4.0 |
||||
*/ |
||||
public class CompositeMessageConverter implements MessageConverter { |
||||
|
||||
private final List<MessageConverter> converters; |
||||
|
||||
private ContentTypeResolver contentTypeResolver; |
||||
|
||||
|
||||
/** |
||||
* Create a new instance with the given {@link MessageConverter}s in turn configuring |
||||
* each with a {@link DefaultContentTypeResolver}. |
||||
*/ |
||||
public CompositeMessageConverter(Collection<MessageConverter> converters) { |
||||
this(new ArrayList<MessageConverter>(converters), new DefaultContentTypeResolver()); |
||||
} |
||||
|
||||
/** |
||||
* Create an instance with the given {@link MessageConverter}s and configure all with |
||||
* the given {@link ContentTypeResolver}. |
||||
*/ |
||||
public CompositeMessageConverter(Collection<MessageConverter> converters, ContentTypeResolver resolver) { |
||||
Assert.notEmpty(converters, "converters is required"); |
||||
Assert.notNull(resolver, "contentTypeResolver is required"); |
||||
this.converters = new ArrayList<MessageConverter>(converters); |
||||
this.contentTypeResolver = resolver; |
||||
applyContentTypeResolver(converters, resolver); |
||||
} |
||||
|
||||
|
||||
private static void applyContentTypeResolver(Collection<MessageConverter> converters, |
||||
ContentTypeResolver resolver) { |
||||
|
||||
for (MessageConverter converter : converters) { |
||||
if (converter instanceof AbstractMessageConverter) { |
||||
((AbstractMessageConverter) converter).setContentTypeResolver(resolver); |
||||
} |
||||
} |
||||
} |
||||
|
||||
|
||||
public void setContentTypeResolver(ContentTypeResolver resolver) { |
||||
this.contentTypeResolver = resolver; |
||||
applyContentTypeResolver(getConverters(), resolver); |
||||
} |
||||
|
||||
public ContentTypeResolver getContentTypeResolver() { |
||||
return this.contentTypeResolver; |
||||
} |
||||
|
||||
public Collection<MessageConverter> getConverters() { |
||||
return this.converters; |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public Object fromMessage(Message<?> message, Class<?> targetClass) { |
||||
for (MessageConverter converter : this.converters) { |
||||
Object result = converter.fromMessage(message, targetClass); |
||||
if (result != null) { |
||||
return result; |
||||
} |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
@Override |
||||
public Message<?> toMessage(Object payload, MessageHeaders headers) { |
||||
for (MessageConverter converter : this.converters) { |
||||
Message<?> result = converter.toMessage(payload, headers); |
||||
if (result != null) { |
||||
return result; |
||||
} |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,60 @@
@@ -0,0 +1,60 @@
|
||||
/* |
||||
* Copyright 2002-2013 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.messaging.support.converter; |
||||
|
||||
import org.springframework.messaging.MessageHeaders; |
||||
import org.springframework.util.MimeType; |
||||
|
||||
|
||||
/** |
||||
* A default {@link ContentTypeResolver} that checks the |
||||
* {@link MessageHeaders#CONTENT_TYPE} header or falls back to a default, if a default is |
||||
* configured. |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
* @since 4.0 |
||||
*/ |
||||
public class DefaultContentTypeResolver implements ContentTypeResolver { |
||||
|
||||
private MimeType defaultMimeType; |
||||
|
||||
|
||||
/** |
||||
* Set the default MIME type to use, if the message headers don't have one. |
||||
* By default this property is set to {@code null}. |
||||
*/ |
||||
public void setDefaultMimeType(MimeType defaultMimeType) { |
||||
this.defaultMimeType = defaultMimeType; |
||||
} |
||||
|
||||
/** |
||||
* Return the default MIME type to use. |
||||
*/ |
||||
public MimeType getDefaultMimeType() { |
||||
return this.defaultMimeType; |
||||
} |
||||
|
||||
@Override |
||||
public MimeType resolve(MessageHeaders headers) { |
||||
MimeType mimeType = null; |
||||
if (headers != null) { |
||||
mimeType = headers.get(MessageHeaders.CONTENT_TYPE, MimeType.class); |
||||
} |
||||
return (mimeType != null) ? mimeType : this.defaultMimeType; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,78 @@
@@ -0,0 +1,78 @@
|
||||
/* |
||||
* Copyright 2002-2013 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.messaging.support.converter; |
||||
|
||||
import java.nio.charset.Charset; |
||||
|
||||
import org.springframework.messaging.Message; |
||||
import org.springframework.messaging.MessageHeaders; |
||||
import org.springframework.util.MimeType; |
||||
|
||||
|
||||
/** |
||||
* A {@link MessageConverter} that supports MIME type "text/plain" with the |
||||
* payload converted to and from a String. |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
* @since 4.0 |
||||
*/ |
||||
public class StringMessageConverter extends AbstractMessageConverter { |
||||
|
||||
private final Charset defaultCharset; |
||||
|
||||
|
||||
public StringMessageConverter() { |
||||
this(Charset.forName("UTF-8")); |
||||
} |
||||
|
||||
public StringMessageConverter(Charset defaultCharset) { |
||||
super(new MimeType("text", "plain", defaultCharset)); |
||||
this.defaultCharset = defaultCharset; |
||||
} |
||||
|
||||
|
||||
@Override |
||||
protected boolean supports(Class<?> clazz) { |
||||
return String.class.equals(clazz); |
||||
} |
||||
|
||||
@Override |
||||
public Object convertFromInternal(Message<?> message, Class<?> targetClass) { |
||||
Charset charset = getContentTypeCharset(getMimeType(message.getHeaders())); |
||||
Object payload = message.getPayload(); |
||||
return (payload instanceof String) ? payload : new String((byte[]) payload, charset); |
||||
} |
||||
|
||||
@Override |
||||
public Object convertToInternal(Object payload, MessageHeaders headers) { |
||||
if (byte[].class.equals(getSerializedPayloadClass())) { |
||||
Charset charset = getContentTypeCharset(getMimeType(headers)); |
||||
payload = ((String) payload).getBytes(charset); |
||||
} |
||||
return payload; |
||||
} |
||||
|
||||
private Charset getContentTypeCharset(MimeType mimeType) { |
||||
if (mimeType != null && mimeType.getCharSet() != null) { |
||||
return mimeType.getCharSet(); |
||||
} |
||||
else { |
||||
return this.defaultCharset; |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,136 @@
@@ -0,0 +1,136 @@
|
||||
/* |
||||
* Copyright 2002-2013 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.messaging.support.converter; |
||||
|
||||
import java.util.Collection; |
||||
import java.util.Collections; |
||||
import java.util.HashMap; |
||||
import java.util.Map; |
||||
|
||||
import org.junit.Before; |
||||
import org.junit.Test; |
||||
import org.springframework.messaging.Message; |
||||
import org.springframework.messaging.MessageHeaders; |
||||
import org.springframework.messaging.support.MessageBuilder; |
||||
import org.springframework.util.MimeType; |
||||
import org.springframework.util.MimeTypeUtils; |
||||
|
||||
import static org.junit.Assert.*; |
||||
|
||||
|
||||
/** |
||||
* Test fixture for {@link AbstractMessageConverter}. |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
*/ |
||||
public class AbstractMessageConverterTests { |
||||
|
||||
private TestMessageConverter converter; |
||||
|
||||
|
||||
@Before |
||||
public void setup() { |
||||
this.converter = new TestMessageConverter(); |
||||
this.converter.setContentTypeResolver(new DefaultContentTypeResolver()); |
||||
} |
||||
|
||||
@Test |
||||
public void supportsTargetClass() { |
||||
Message<String> message = MessageBuilder.withPayload("ABC").build(); |
||||
|
||||
assertEquals("success-from", this.converter.fromMessage(message, String.class)); |
||||
assertNull(this.converter.fromMessage(message, Integer.class)); |
||||
} |
||||
|
||||
@Test |
||||
public void supportsMimeType() { |
||||
Message<String> message = MessageBuilder.withPayload( |
||||
"ABC").setHeader(MessageHeaders.CONTENT_TYPE, MimeTypeUtils.TEXT_PLAIN).build(); |
||||
|
||||
assertEquals("success-from", this.converter.fromMessage(message, String.class)); |
||||
} |
||||
|
||||
@Test |
||||
public void supportsMimeTypeNotSupported() { |
||||
Message<String> message = MessageBuilder.withPayload( |
||||
"ABC").setHeader(MessageHeaders.CONTENT_TYPE, MimeTypeUtils.APPLICATION_JSON).build(); |
||||
|
||||
assertNull(this.converter.fromMessage(message, String.class)); |
||||
} |
||||
|
||||
@Test |
||||
public void supportsMimeTypeNotSpecified() { |
||||
Message<String> message = MessageBuilder.withPayload("ABC").build(); |
||||
assertEquals("success-from", this.converter.fromMessage(message, String.class)); |
||||
} |
||||
|
||||
@Test |
||||
public void supportsMimeTypeNoneConfigured() { |
||||
|
||||
Message<String> message = MessageBuilder.withPayload( |
||||
"ABC").setHeader(MessageHeaders.CONTENT_TYPE, MimeTypeUtils.APPLICATION_JSON).build(); |
||||
|
||||
this.converter = new TestMessageConverter(Collections.<MimeType>emptyList()); |
||||
this.converter.setContentTypeResolver(new DefaultContentTypeResolver()); |
||||
|
||||
assertEquals("success-from", this.converter.fromMessage(message, String.class)); |
||||
} |
||||
|
||||
@Test |
||||
public void toMessageHeadersCopied() { |
||||
Map<String, Object> map = new HashMap<String, Object>(); |
||||
map.put("foo", "bar"); |
||||
MessageHeaders headers = new MessageHeaders(map ); |
||||
Message<?> message = this.converter.toMessage("ABC", headers); |
||||
|
||||
assertEquals("bar", message.getHeaders().get("foo")); |
||||
} |
||||
|
||||
@Test |
||||
public void toMessageContentTypeHeader() { |
||||
Message<?> message = this.converter.toMessage("ABC", null); |
||||
assertEquals(MimeTypeUtils.TEXT_PLAIN, message.getHeaders().get(MessageHeaders.CONTENT_TYPE)); |
||||
} |
||||
|
||||
|
||||
private static class TestMessageConverter extends AbstractMessageConverter { |
||||
|
||||
public TestMessageConverter() { |
||||
super(MimeTypeUtils.TEXT_PLAIN); |
||||
} |
||||
|
||||
public TestMessageConverter(Collection<MimeType> supportedMimeTypes) { |
||||
super(supportedMimeTypes); |
||||
} |
||||
|
||||
@Override |
||||
protected boolean supports(Class<?> clazz) { |
||||
return String.class.equals(clazz); |
||||
} |
||||
|
||||
@Override |
||||
public Object convertFromInternal(Message<?> message, Class<?> targetClass) { |
||||
return "success-from"; |
||||
} |
||||
|
||||
@Override |
||||
public Object convertToInternal(Object payload, MessageHeaders headers) { |
||||
return "success-to"; |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,70 @@
@@ -0,0 +1,70 @@
|
||||
/* |
||||
* Copyright 2002-2013 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.messaging.support.converter; |
||||
|
||||
import java.util.Collections; |
||||
import java.util.HashMap; |
||||
import java.util.Map; |
||||
|
||||
import org.junit.Before; |
||||
import org.junit.Test; |
||||
import org.springframework.messaging.MessageHeaders; |
||||
import org.springframework.util.MimeTypeUtils; |
||||
|
||||
import static org.junit.Assert.*; |
||||
|
||||
|
||||
/** |
||||
* Test fixture for {@link DefaultContentTypeResolver}. |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
*/ |
||||
public class DefaultContentTypeResolverTests { |
||||
|
||||
private DefaultContentTypeResolver resolver; |
||||
|
||||
|
||||
@Before |
||||
public void setup() { |
||||
this.resolver = new DefaultContentTypeResolver(); |
||||
} |
||||
|
||||
@Test |
||||
public void resolve() { |
||||
Map<String, Object> map = new HashMap<String, Object>(); |
||||
map.put(MessageHeaders.CONTENT_TYPE, MimeTypeUtils.APPLICATION_JSON); |
||||
MessageHeaders headers = new MessageHeaders(map); |
||||
|
||||
assertEquals(MimeTypeUtils.APPLICATION_JSON, this.resolver.resolve(headers)); |
||||
} |
||||
|
||||
@Test |
||||
public void resolveNoContentTypeHeader() { |
||||
MessageHeaders headers = new MessageHeaders(Collections.<String, Object>emptyMap()); |
||||
|
||||
assertNull(this.resolver.resolve(headers)); |
||||
} |
||||
|
||||
@Test |
||||
public void resolveFromDefaultMimeType() { |
||||
this.resolver.setDefaultMimeType(MimeTypeUtils.APPLICATION_JSON); |
||||
MessageHeaders headers = new MessageHeaders(Collections.<String, Object>emptyMap()); |
||||
|
||||
assertEquals(MimeTypeUtils.APPLICATION_JSON, this.resolver.resolve(headers)); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,215 @@
@@ -0,0 +1,215 @@
|
||||
/* |
||||
* Copyright 2002-2013 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.messaging.support.converter; |
||||
|
||||
import java.io.IOException; |
||||
import java.nio.charset.Charset; |
||||
import java.util.Arrays; |
||||
import java.util.HashMap; |
||||
import java.util.Map; |
||||
|
||||
import org.junit.Before; |
||||
import org.junit.Test; |
||||
import org.springframework.messaging.Message; |
||||
import org.springframework.messaging.MessageHeaders; |
||||
import org.springframework.messaging.support.MessageBuilder; |
||||
import org.springframework.util.MimeType; |
||||
|
||||
import static org.junit.Assert.*; |
||||
|
||||
|
||||
/** |
||||
* Test fixture for {@link MappingJackson2MessageConverter}. |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
*/ |
||||
public class MappingJackson2MessageConverterTests { |
||||
|
||||
private static Charset UTF_8 = Charset.forName("UTF-8"); |
||||
|
||||
private MappingJackson2MessageConverter converter; |
||||
|
||||
|
||||
@Before |
||||
public void setup() { |
||||
this.converter = new MappingJackson2MessageConverter(); |
||||
this.converter.setContentTypeResolver(new DefaultContentTypeResolver()); |
||||
} |
||||
|
||||
@Test |
||||
public void fromMessage() throws Exception { |
||||
String payload = "{\"bytes\":\"AQI=\",\"array\":[\"Foo\",\"Bar\"]," |
||||
+ "\"number\":42,\"string\":\"Foo\",\"bool\":true,\"fraction\":42.0}"; |
||||
Message<?> message = MessageBuilder.withPayload(payload.getBytes(UTF_8)).build(); |
||||
MyBean actual = (MyBean) this.converter.fromMessage(message, MyBean.class); |
||||
|
||||
assertEquals("Foo", actual.getString()); |
||||
assertEquals(42, actual.getNumber()); |
||||
assertEquals(42F, actual.getFraction(), 0F); |
||||
assertArrayEquals(new String[]{"Foo", "Bar"}, actual.getArray()); |
||||
assertTrue(actual.isBool()); |
||||
assertArrayEquals(new byte[]{0x1, 0x2}, actual.getBytes()); |
||||
} |
||||
|
||||
@Test |
||||
public void fromMessageUntyped() throws Exception { |
||||
String payload = "{\"bytes\":\"AQI=\",\"array\":[\"Foo\",\"Bar\"]," |
||||
+ "\"number\":42,\"string\":\"Foo\",\"bool\":true,\"fraction\":42.0}"; |
||||
Message<?> message = MessageBuilder.withPayload(payload.getBytes(UTF_8)).build(); |
||||
@SuppressWarnings("unchecked") |
||||
HashMap<String, Object> actual = (HashMap<String, Object>) this.converter.fromMessage(message, HashMap.class); |
||||
|
||||
assertEquals("Foo", actual.get("string")); |
||||
assertEquals(42, actual.get("number")); |
||||
assertEquals(42D, (Double) actual.get("fraction"), 0D); |
||||
assertEquals(Arrays.asList("Foo", "Bar"), actual.get("array")); |
||||
assertEquals(Boolean.TRUE, actual.get("bool")); |
||||
assertEquals("AQI=", actual.get("bytes")); |
||||
} |
||||
|
||||
@Test(expected = MessageConversionException.class) |
||||
public void fromMessageInvalidJson() throws Exception { |
||||
String payload = "FooBar"; |
||||
Message<?> message = MessageBuilder.withPayload(payload.getBytes(UTF_8)).build(); |
||||
this.converter.fromMessage(message, MyBean.class); |
||||
} |
||||
|
||||
@Test(expected = MessageConversionException.class) |
||||
public void fromMessageValidJsonWithUnknownProperty() throws IOException { |
||||
String payload = "{\"string\":\"string\",\"unknownProperty\":\"value\"}"; |
||||
Message<?> message = MessageBuilder.withPayload(payload.getBytes(UTF_8)).build(); |
||||
this.converter.fromMessage(message, MyBean.class); |
||||
} |
||||
|
||||
@Test |
||||
public void toMessage() throws Exception { |
||||
MyBean payload = new MyBean(); |
||||
payload.setString("Foo"); |
||||
payload.setNumber(42); |
||||
payload.setFraction(42F); |
||||
payload.setArray(new String[]{"Foo", "Bar"}); |
||||
payload.setBool(true); |
||||
payload.setBytes(new byte[]{0x1, 0x2}); |
||||
|
||||
Message<?> message = this.converter.toMessage(payload, null); |
||||
String actual = new String((byte[]) message.getPayload(), UTF_8); |
||||
|
||||
assertTrue(actual.contains("\"string\":\"Foo\"")); |
||||
assertTrue(actual.contains("\"number\":42")); |
||||
assertTrue(actual.contains("fraction\":42.0")); |
||||
assertTrue(actual.contains("\"array\":[\"Foo\",\"Bar\"]")); |
||||
assertTrue(actual.contains("\"bool\":true")); |
||||
assertTrue(actual.contains("\"bytes\":\"AQI=\"")); |
||||
assertEquals("Invalid content-type", new MimeType("application", "json", UTF_8), |
||||
message.getHeaders().get(MessageHeaders.CONTENT_TYPE, MimeType.class)); |
||||
} |
||||
|
||||
@Test |
||||
public void toMessageUtf16() { |
||||
Charset utf16 = Charset.forName("UTF-16BE"); |
||||
MimeType contentType = new MimeType("application", "json", utf16); |
||||
Map<String, Object> map = new HashMap<>(); |
||||
map.put(MessageHeaders.CONTENT_TYPE, contentType); |
||||
MessageHeaders headers = new MessageHeaders(map); |
||||
String payload = "H\u00e9llo W\u00f6rld"; |
||||
Message<?> message = this.converter.toMessage(payload, headers); |
||||
|
||||
assertEquals("\"" + payload + "\"", new String((byte[]) message.getPayload(), utf16)); |
||||
assertEquals(contentType, message.getHeaders().get(MessageHeaders.CONTENT_TYPE)); |
||||
} |
||||
|
||||
@Test |
||||
public void toMessageUtf16String() { |
||||
this.converter.setSerializedPayloadClass(String.class); |
||||
|
||||
Charset utf16 = Charset.forName("UTF-16BE"); |
||||
MimeType contentType = new MimeType("application", "json", utf16); |
||||
Map<String, Object> map = new HashMap<>(); |
||||
map.put(MessageHeaders.CONTENT_TYPE, contentType); |
||||
MessageHeaders headers = new MessageHeaders(map); |
||||
String payload = "H\u00e9llo W\u00f6rld"; |
||||
Message<?> message = this.converter.toMessage(payload, headers); |
||||
|
||||
assertEquals("\"" + payload + "\"", message.getPayload()); |
||||
assertEquals(contentType, message.getHeaders().get(MessageHeaders.CONTENT_TYPE)); |
||||
} |
||||
|
||||
|
||||
public static class MyBean { |
||||
|
||||
private String string; |
||||
|
||||
private int number; |
||||
|
||||
private float fraction; |
||||
|
||||
private String[] array; |
||||
|
||||
private boolean bool; |
||||
|
||||
private byte[] bytes; |
||||
|
||||
public byte[] getBytes() { |
||||
return bytes; |
||||
} |
||||
|
||||
public void setBytes(byte[] bytes) { |
||||
this.bytes = bytes; |
||||
} |
||||
|
||||
public boolean isBool() { |
||||
return bool; |
||||
} |
||||
|
||||
public void setBool(boolean bool) { |
||||
this.bool = bool; |
||||
} |
||||
|
||||
public String getString() { |
||||
return string; |
||||
} |
||||
|
||||
public void setString(String string) { |
||||
this.string = string; |
||||
} |
||||
|
||||
public int getNumber() { |
||||
return number; |
||||
} |
||||
|
||||
public void setNumber(int number) { |
||||
this.number = number; |
||||
} |
||||
|
||||
public float getFraction() { |
||||
return fraction; |
||||
} |
||||
|
||||
public void setFraction(float fraction) { |
||||
this.fraction = fraction; |
||||
} |
||||
|
||||
public String[] getArray() { |
||||
return array; |
||||
} |
||||
|
||||
public void setArray(String[] array) { |
||||
this.array = array; |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,113 @@
@@ -0,0 +1,113 @@
|
||||
/* |
||||
* Copyright 2002-2013 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.messaging.support.converter; |
||||
|
||||
import java.nio.charset.Charset; |
||||
import java.util.HashMap; |
||||
import java.util.Map; |
||||
|
||||
import org.junit.Before; |
||||
import org.junit.Test; |
||||
import org.springframework.messaging.Message; |
||||
import org.springframework.messaging.MessageHeaders; |
||||
import org.springframework.messaging.support.MessageBuilder; |
||||
import org.springframework.util.MimeType; |
||||
import org.springframework.util.MimeTypeUtils; |
||||
|
||||
import static org.junit.Assert.*; |
||||
|
||||
|
||||
/** |
||||
* Test fixture for {@link StringMessageConverter}. |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
*/ |
||||
public class StringMessageConverterTests { |
||||
|
||||
private StringMessageConverter converter; |
||||
|
||||
|
||||
@Before |
||||
public void setUp() { |
||||
this.converter = new StringMessageConverter(); |
||||
this.converter.setContentTypeResolver(new DefaultContentTypeResolver()); |
||||
} |
||||
|
||||
|
||||
@Test |
||||
public void fromByteArrayMessage() { |
||||
Message<byte[]> message = MessageBuilder.withPayload( |
||||
"ABC".getBytes()).setHeader(MessageHeaders.CONTENT_TYPE, MimeTypeUtils.TEXT_PLAIN).build(); |
||||
assertEquals("ABC", this.converter.fromMessage(message, String.class)); |
||||
} |
||||
|
||||
@Test |
||||
public void fromStringMessage() { |
||||
Message<String> message = MessageBuilder.withPayload( |
||||
"ABC").setHeader(MessageHeaders.CONTENT_TYPE, MimeTypeUtils.TEXT_PLAIN).build(); |
||||
assertEquals("ABC", this.converter.fromMessage(message, String.class)); |
||||
} |
||||
|
||||
@Test |
||||
public void fromMessageNoContentTypeHeader() { |
||||
Message<byte[]> message = MessageBuilder.withPayload("ABC".getBytes()).build(); |
||||
assertEquals("ABC", this.converter.fromMessage(message, String.class)); |
||||
} |
||||
|
||||
@Test |
||||
public void fromMessageCharset() { |
||||
Charset iso88591 = Charset.forName("ISO-8859-1"); |
||||
String payload = "H\u00e9llo W\u00f6rld"; |
||||
Message<byte[]> message = MessageBuilder.withPayload(payload.getBytes(iso88591)) |
||||
.setHeader(MessageHeaders.CONTENT_TYPE, new MimeType("text", "plain", iso88591)).build(); |
||||
|
||||
assertEquals(payload, this.converter.fromMessage(message, String.class)); |
||||
} |
||||
|
||||
@Test |
||||
public void fromMessageDefaultCharset() { |
||||
Charset utf8 = Charset.forName("UTF-8"); |
||||
String payload = "H\u00e9llo W\u00f6rld"; |
||||
Message<byte[]> message = MessageBuilder.withPayload(payload.getBytes(utf8)).build(); |
||||
|
||||
assertEquals(payload, this.converter.fromMessage(message, String.class)); |
||||
} |
||||
|
||||
@Test |
||||
public void fromMessageTargetClassNotSupported() { |
||||
Message<byte[]> message = MessageBuilder.withPayload("ABC".getBytes()).build(); |
||||
assertNull(this.converter.fromMessage(message, Integer.class)); |
||||
} |
||||
|
||||
@Test |
||||
public void fromMessageByteArray() { |
||||
Message<byte[]> message = MessageBuilder.withPayload( |
||||
"ABC".getBytes()).setHeader(MessageHeaders.CONTENT_TYPE, MimeTypeUtils.TEXT_PLAIN).build(); |
||||
assertEquals("ABC", this.converter.fromMessage(message, String.class)); |
||||
} |
||||
|
||||
@Test |
||||
public void toMessage() { |
||||
Map<String, Object> map = new HashMap<String, Object>(); |
||||
map.put(MessageHeaders.CONTENT_TYPE, MimeTypeUtils.TEXT_PLAIN); |
||||
MessageHeaders headers = new MessageHeaders(map); |
||||
Message<?> message = this.converter.toMessage("ABC", headers); |
||||
|
||||
assertEquals("ABC", new String(((byte[]) message.getPayload()))); |
||||
} |
||||
|
||||
} |
||||
Loading…
Reference in new issue