Merge pull request #88 from aspan/AMQP-300
This commit is contained in:
@@ -42,6 +42,7 @@ subprojects { subproject ->
|
||||
commonsIoVersion = '1.4'
|
||||
erlangOtpVersion = '1.5.3'
|
||||
jacksonVersion = '1.4.3'
|
||||
jackson2Version = '2.0.1'
|
||||
junitVersion = '4.8.2'
|
||||
log4jVersion = '1.2.15'
|
||||
mockitoVersion = '1.8.4'
|
||||
@@ -122,7 +123,8 @@ project('spring-amqp') {
|
||||
compile ("org.springframework:spring-context:$springVersion", optional)
|
||||
compile ("org.codehaus.jackson:jackson-core-asl:$jacksonVersion", optional)
|
||||
compile ("org.codehaus.jackson:jackson-mapper-asl:$jacksonVersion", optional)
|
||||
|
||||
compile ("com.fasterxml.jackson.core:jackson-core:$jackson2Version", optional)
|
||||
compile ("com.fasterxml.jackson.core:jackson-databind:$jackson2Version", optional)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* 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.amqp.support.converter;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.amqp.core.MessageProperties;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
|
||||
/**
|
||||
* @author Mark Pollack
|
||||
* @author Sam Nelson
|
||||
* @author Andreas Asplund
|
||||
*/
|
||||
public abstract class AbstractJavaTypeMapper implements InitializingBean {
|
||||
public static final String DEFAULT_CLASSID_FIELD_NAME = "__TypeId__";
|
||||
public static final String DEFAULT_CONTENT_CLASSID_FIELD_NAME = "__ContentTypeId__";
|
||||
public static final String DEFAULT_KEY_CLASSID_FIELD_NAME = "__KeyTypeId__";
|
||||
|
||||
private Map<String, Class<?>> idClassMapping = new HashMap<String, Class<?>>();
|
||||
private final Map<Class<?>, String> classIdMapping = new HashMap<Class<?>, String>();
|
||||
|
||||
public String getClassIdFieldName() {
|
||||
return DEFAULT_CLASSID_FIELD_NAME;
|
||||
}
|
||||
|
||||
public String getContentClassIdFieldName() {
|
||||
return DEFAULT_CONTENT_CLASSID_FIELD_NAME;
|
||||
}
|
||||
|
||||
public String getKeyClassIdFieldName() {
|
||||
return DEFAULT_KEY_CLASSID_FIELD_NAME;
|
||||
}
|
||||
|
||||
public void setIdClassMapping(Map<String, Class<?>> idClassMapping) {
|
||||
this.idClassMapping = idClassMapping;
|
||||
}
|
||||
|
||||
protected void addHeader(MessageProperties properties, String headerName,
|
||||
Class<?> clazz) {
|
||||
if (classIdMapping.containsKey(clazz)) {
|
||||
properties.getHeaders().put(headerName, classIdMapping.get(clazz));
|
||||
}
|
||||
else {
|
||||
properties.getHeaders().put(headerName, clazz.getName());
|
||||
}
|
||||
}
|
||||
|
||||
protected String retrieveHeader(MessageProperties properties,
|
||||
String headerName) {
|
||||
Map<String, Object> headers = properties.getHeaders();
|
||||
Object classIdFieldNameValue = headers.get(headerName);
|
||||
String classId = null;
|
||||
if (classIdFieldNameValue != null) {
|
||||
classId = classIdFieldNameValue.toString();
|
||||
}
|
||||
if (classId == null) {
|
||||
throw new MessageConversionException(
|
||||
"failed to convert Message content. Could not resolve "
|
||||
+ headerName + " in header");
|
||||
}
|
||||
return classId;
|
||||
}
|
||||
|
||||
private void validateIdTypeMapping() {
|
||||
Map<String, Class<?>> finalIdClassMapping = new HashMap<String, Class<?>>();
|
||||
for (Map.Entry<String, Class<?>> entry : idClassMapping.entrySet()) {
|
||||
String id = entry.getKey();
|
||||
Class<?> clazz = entry.getValue();
|
||||
finalIdClassMapping.put(id, clazz);
|
||||
classIdMapping.put(clazz, id);
|
||||
}
|
||||
this.idClassMapping = finalIdClassMapping;
|
||||
}
|
||||
|
||||
public Map<String, Class<?>> getIdClassMapping() {
|
||||
return Collections.unmodifiableMap(idClassMapping);
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
validateIdTypeMapping();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright 2002-2012 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.amqp.support.converter;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Mark Pollack
|
||||
* @author James Carr
|
||||
* @author Dave Syer
|
||||
* @author Sam Nelson
|
||||
* @author Andreas Asplund
|
||||
*/
|
||||
public abstract class AbstractJsonMessageConverter extends AbstractMessageConverter {
|
||||
public static final String DEFAULT_CHARSET = "UTF-8";
|
||||
|
||||
private volatile String defaultCharset = DEFAULT_CHARSET;
|
||||
|
||||
private ClassMapper classMapper = null;
|
||||
|
||||
public ClassMapper getClassMapper() {
|
||||
return classMapper;
|
||||
|
||||
}
|
||||
|
||||
public void setClassMapper(ClassMapper classMapper) {
|
||||
this.classMapper = classMapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the default charset to use when converting to or from text-based
|
||||
* Message body content. If not specified, the charset will be "UTF-8".
|
||||
*/
|
||||
public void setDefaultCharset(String defaultCharset) {
|
||||
this.defaultCharset = (defaultCharset != null) ? defaultCharset
|
||||
: DEFAULT_CHARSET;
|
||||
}
|
||||
|
||||
public String getDefaultCharset() {
|
||||
return defaultCharset;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* 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.amqp.support.converter;
|
||||
|
||||
import com.fasterxml.jackson.databind.JavaType;
|
||||
import com.fasterxml.jackson.databind.type.CollectionType;
|
||||
import com.fasterxml.jackson.databind.type.MapType;
|
||||
import com.fasterxml.jackson.databind.type.TypeFactory;
|
||||
import org.springframework.amqp.core.MessageProperties;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* @author Mark Pollack
|
||||
* @author Sam Nelson
|
||||
* @author Andreas Asplund
|
||||
*/
|
||||
public class DefaultJackson2JavaTypeMapper extends AbstractJavaTypeMapper implements Jackson2JavaTypeMapper, ClassMapper {
|
||||
|
||||
public JavaType toJavaType(MessageProperties properties) {
|
||||
JavaType classType = getClassIdType(retrieveHeader(properties,
|
||||
getClassIdFieldName()));
|
||||
if (!classType.isContainerType()) {
|
||||
return classType;
|
||||
}
|
||||
|
||||
JavaType contentClassType = getClassIdType(retrieveHeader(properties,
|
||||
getContentClassIdFieldName()));
|
||||
if (classType.getKeyType() == null) {
|
||||
return CollectionType.construct(
|
||||
classType.getRawClass(),
|
||||
contentClassType);
|
||||
}
|
||||
|
||||
JavaType keyClassType = getClassIdType(retrieveHeader(properties,
|
||||
getKeyClassIdFieldName()));
|
||||
JavaType mapType = MapType.construct(
|
||||
classType.getRawClass(), keyClassType,
|
||||
contentClassType);
|
||||
return mapType;
|
||||
|
||||
}
|
||||
|
||||
private JavaType getClassIdType(String classId) {
|
||||
if (getIdClassMapping().containsKey(classId)) {
|
||||
return TypeFactory.defaultInstance().constructType(getIdClassMapping().get(classId));
|
||||
}
|
||||
|
||||
try {
|
||||
return TypeFactory.defaultInstance().constructType(ClassUtils.forName(classId, getClass()
|
||||
.getClassLoader()));
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new MessageConversionException(
|
||||
"failed to resolve class name. Class not found [" + classId
|
||||
+ "]", e);
|
||||
} catch (LinkageError e) {
|
||||
throw new MessageConversionException(
|
||||
"failed to resolve class name. Linkage error [" + classId
|
||||
+ "]", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void fromJavaType(JavaType javaType, MessageProperties properties) {
|
||||
addHeader(properties, getClassIdFieldName(),
|
||||
(Class<?>) javaType.getRawClass());
|
||||
|
||||
if (javaType.isContainerType()) {
|
||||
addHeader(properties, getContentClassIdFieldName(), javaType
|
||||
.getContentType().getRawClass());
|
||||
}
|
||||
|
||||
if (javaType.getKeyType() != null) {
|
||||
addHeader(properties, getKeyClassIdFieldName(), javaType
|
||||
.getKeyType().getRawClass());
|
||||
}
|
||||
}
|
||||
|
||||
public void fromClass(Class<?> clazz, MessageProperties properties) {
|
||||
fromJavaType(TypeFactory.defaultInstance().constructType(clazz), properties);
|
||||
|
||||
}
|
||||
|
||||
public Class<?> toClass(MessageProperties properties) {
|
||||
return toJavaType(properties).getRawClass();
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* 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,
|
||||
@@ -8,45 +8,23 @@
|
||||
*/
|
||||
package org.springframework.amqp.support.converter;
|
||||
|
||||
/**
|
||||
* @author Mark Pollack
|
||||
* @author Sam Nelson
|
||||
*/
|
||||
import static org.codehaus.jackson.map.type.TypeFactory.collectionType;
|
||||
import static org.codehaus.jackson.map.type.TypeFactory.mapType;
|
||||
import static org.codehaus.jackson.map.type.TypeFactory.type;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.codehaus.jackson.type.JavaType;
|
||||
import org.springframework.amqp.core.MessageProperties;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
public class DefaultJavaTypeMapper implements JavaTypeMapper, ClassMapper,
|
||||
InitializingBean {
|
||||
|
||||
public static final String DEFAULT_CLASSID_FIELD_NAME = "__TypeId__";
|
||||
public static final String DEFAULT_CONTENT_CLASSID_FIELD_NAME = "__ContentTypeId__";
|
||||
public static final String DEFAULT_KEY_CLASSID_FIELD_NAME = "__KeyTypeId__";
|
||||
|
||||
private Map<String, Class<?>> idClassMapping = new HashMap<String, Class<?>>();
|
||||
private Map<Class<?>, String> classIdMapping = new HashMap<Class<?>, String>();
|
||||
|
||||
public String getClassIdFieldName() {
|
||||
return DEFAULT_CLASSID_FIELD_NAME;
|
||||
}
|
||||
|
||||
public String getContentClassIdFieldName() {
|
||||
return DEFAULT_CONTENT_CLASSID_FIELD_NAME;
|
||||
}
|
||||
|
||||
public String getKeyClassIdFieldName() {
|
||||
return DEFAULT_KEY_CLASSID_FIELD_NAME;
|
||||
}
|
||||
/**
|
||||
* @author Mark Pollack
|
||||
* @author Sam Nelson
|
||||
* @author Andreas Asplund
|
||||
*/
|
||||
public class DefaultJavaTypeMapper extends AbstractJavaTypeMapper implements JavaTypeMapper, ClassMapper {
|
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
public JavaType toJavaType(MessageProperties properties) {
|
||||
@@ -74,47 +52,29 @@ public class DefaultJavaTypeMapper implements JavaTypeMapper, ClassMapper,
|
||||
}
|
||||
|
||||
private JavaType getClassIdType(String classId) {
|
||||
if (this.idClassMapping.containsKey(classId)) {
|
||||
return type(idClassMapping.get(classId));
|
||||
if (getIdClassMapping().containsKey(classId)) {
|
||||
return type(getIdClassMapping().get(classId));
|
||||
}
|
||||
|
||||
try {
|
||||
return type(ClassUtils.forName(classId, getClass()
|
||||
.getClassLoader()));
|
||||
} catch (ClassNotFoundException e) {
|
||||
}
|
||||
catch (ClassNotFoundException e) {
|
||||
throw new MessageConversionException(
|
||||
"failed to resolve class name. Class not found [" + classId
|
||||
+ "]", e);
|
||||
} catch (LinkageError e) {
|
||||
}
|
||||
catch (LinkageError e) {
|
||||
throw new MessageConversionException(
|
||||
"failed to resolve class name. Linkage error [" + classId
|
||||
+ "]", e);
|
||||
}
|
||||
}
|
||||
|
||||
private String retrieveHeader(MessageProperties properties,
|
||||
String headerName) {
|
||||
Map<String, Object> headers = properties.getHeaders();
|
||||
Object classIdFieldNameValue = headers.get(headerName);
|
||||
String classId = null;
|
||||
if (classIdFieldNameValue != null) {
|
||||
classId = classIdFieldNameValue.toString();
|
||||
}
|
||||
if (classId == null) {
|
||||
throw new MessageConversionException(
|
||||
"failed to convert Message content. Could not resolve "
|
||||
+ headerName + " in header");
|
||||
}
|
||||
return classId;
|
||||
}
|
||||
|
||||
public void setIdClassMapping(Map<String, Class<?>> idClassMapping) {
|
||||
this.idClassMapping = idClassMapping;
|
||||
}
|
||||
|
||||
public void fromJavaType(JavaType javaType, MessageProperties properties) {
|
||||
addHeader(properties, getClassIdFieldName(),
|
||||
(Class<?>) javaType.getRawClass());
|
||||
javaType.getRawClass());
|
||||
|
||||
if (javaType.isContainerType()) {
|
||||
addHeader(properties, getContentClassIdFieldName(), javaType
|
||||
@@ -127,30 +87,6 @@ public class DefaultJavaTypeMapper implements JavaTypeMapper, ClassMapper,
|
||||
}
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
validateIdTypeMapping();
|
||||
}
|
||||
|
||||
private void addHeader(MessageProperties properties, String headerName,
|
||||
Class<?> clazz) {
|
||||
if (classIdMapping.containsKey(clazz)) {
|
||||
properties.getHeaders().put(headerName, classIdMapping.get(clazz));
|
||||
} else {
|
||||
properties.getHeaders().put(headerName, clazz.getName());
|
||||
}
|
||||
}
|
||||
|
||||
private void validateIdTypeMapping() {
|
||||
Map<String, Class<?>> finalIdClassMapping = new HashMap<String, Class<?>>();
|
||||
for (Entry<String, Class<?>> entry : idClassMapping.entrySet()) {
|
||||
String id = entry.getKey();
|
||||
Class<?> clazz = entry.getValue();
|
||||
finalIdClassMapping.put(id, clazz);
|
||||
classIdMapping.put(clazz, id);
|
||||
}
|
||||
this.idClassMapping = finalIdClassMapping;
|
||||
}
|
||||
|
||||
public void fromClass(Class<?> clazz, MessageProperties properties) {
|
||||
fromJavaType(type(clazz), properties);
|
||||
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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.amqp.support.converter;
|
||||
|
||||
import com.fasterxml.jackson.databind.JavaType;
|
||||
import org.springframework.amqp.core.MessageProperties;
|
||||
|
||||
/**
|
||||
* Strategy for setting metadata on messages such that one can create the class that needs to be instantiated when
|
||||
* receiving a message.
|
||||
*
|
||||
* @author Mark Pollack
|
||||
* @author James Carr
|
||||
* @author Sam Nelson
|
||||
* @author Andreas Asplund
|
||||
*/
|
||||
public interface Jackson2JavaTypeMapper extends ClassMapper {
|
||||
|
||||
void fromJavaType(JavaType javaType, MessageProperties properties);
|
||||
|
||||
JavaType toJavaType(MessageProperties properties);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
/*
|
||||
* 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.amqp.support.converter;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.amqp.core.Message;
|
||||
import org.springframework.amqp.core.MessageProperties;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonParseException;
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.JavaType;
|
||||
import com.fasterxml.jackson.databind.JsonMappingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
/**
|
||||
* JSON converter that uses the Jackson 2 Json library.
|
||||
*
|
||||
* @author Mark Pollack
|
||||
* @author James Carr
|
||||
* @author Dave Syer
|
||||
* @author Sam Nelson
|
||||
* @author Andreas Asplund
|
||||
*/
|
||||
public class Jackson2JsonMessageConverter extends AbstractJsonMessageConverter {
|
||||
|
||||
private static Log log = LogFactory.getLog(Jackson2JsonMessageConverter.class);
|
||||
|
||||
private ObjectMapper jsonObjectMapper = new ObjectMapper();
|
||||
|
||||
private Jackson2JavaTypeMapper javaTypeMapper = new DefaultJackson2JavaTypeMapper();
|
||||
|
||||
public Jackson2JsonMessageConverter() {
|
||||
super();
|
||||
initializeJsonObjectMapper();
|
||||
}
|
||||
|
||||
public Jackson2JavaTypeMapper getJavaTypeMapper() {
|
||||
return javaTypeMapper;
|
||||
}
|
||||
|
||||
public void setJavaTypeMapper(Jackson2JavaTypeMapper javaTypeMapper) {
|
||||
this.javaTypeMapper = javaTypeMapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* The {@link com.fasterxml.jackson.databind.ObjectMapper} to use instead of using the default. An
|
||||
* alternative to injecting a mapper is to extend this class and override
|
||||
* {@link #initializeJsonObjectMapper()}.
|
||||
*
|
||||
* @param jsonObjectMapper
|
||||
* the object mapper to set
|
||||
*/
|
||||
public void setJsonObjectMapper(ObjectMapper jsonObjectMapper) {
|
||||
this.jsonObjectMapper = jsonObjectMapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclass and override to customize.
|
||||
*/
|
||||
protected void initializeJsonObjectMapper() {
|
||||
jsonObjectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object fromMessage(Message message)
|
||||
throws MessageConversionException {
|
||||
Object content = null;
|
||||
MessageProperties properties = message.getMessageProperties();
|
||||
if (properties != null) {
|
||||
String contentType = properties.getContentType();
|
||||
if (contentType != null && contentType.contains("json")) {
|
||||
String encoding = properties.getContentEncoding();
|
||||
if (encoding == null) {
|
||||
encoding = getDefaultCharset();
|
||||
}
|
||||
try {
|
||||
|
||||
if (getClassMapper() == null) {
|
||||
JavaType targetJavaType = getJavaTypeMapper()
|
||||
.toJavaType(message.getMessageProperties());
|
||||
content = convertBytesToObject(message.getBody(),
|
||||
encoding, targetJavaType);
|
||||
}
|
||||
else {
|
||||
Class<?> targetClass = getClassMapper().toClass(
|
||||
message.getMessageProperties());
|
||||
content = convertBytesToObject(message.getBody(),
|
||||
encoding, targetClass);
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new MessageConversionException(
|
||||
"Failed to convert Message content", e);
|
||||
}
|
||||
}
|
||||
else {
|
||||
log.warn("Could not convert incoming message with content-type ["
|
||||
+ contentType + "]");
|
||||
}
|
||||
}
|
||||
if (content == null) {
|
||||
content = message.getBody();
|
||||
}
|
||||
return content;
|
||||
}
|
||||
|
||||
private Object convertBytesToObject(byte[] body, String encoding,
|
||||
JavaType targetJavaType) throws JsonParseException,
|
||||
JsonMappingException, IOException {
|
||||
String contentAsString = new String(body, encoding);
|
||||
return jsonObjectMapper.readValue(contentAsString, targetJavaType);
|
||||
}
|
||||
|
||||
private Object convertBytesToObject(byte[] body, String encoding,
|
||||
Class<?> targetClass) throws JsonParseException,
|
||||
JsonMappingException, IOException {
|
||||
String contentAsString = new String(body, encoding);
|
||||
return jsonObjectMapper.readValue(contentAsString, jsonObjectMapper.constructType(targetClass));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Message createMessage(Object objectToConvert,
|
||||
MessageProperties messageProperties)
|
||||
throws MessageConversionException {
|
||||
byte[] bytes = null;
|
||||
try {
|
||||
String jsonString = jsonObjectMapper
|
||||
.writeValueAsString(objectToConvert);
|
||||
bytes = jsonString.getBytes(getDefaultCharset());
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new MessageConversionException(
|
||||
"Failed to convert Message content", e);
|
||||
}
|
||||
messageProperties.setContentType(MessageProperties.CONTENT_TYPE_JSON);
|
||||
messageProperties.setContentEncoding(getDefaultCharset());
|
||||
if (bytes != null) {
|
||||
messageProperties.setContentLength(bytes.length);
|
||||
}
|
||||
|
||||
if (getClassMapper() == null) {
|
||||
getJavaTypeMapper().fromJavaType(jsonObjectMapper.constructType(objectToConvert.getClass()),
|
||||
messageProperties);
|
||||
|
||||
}
|
||||
else {
|
||||
getClassMapper().fromClass(objectToConvert.getClass(),
|
||||
messageProperties);
|
||||
|
||||
}
|
||||
|
||||
return new Message(bytes, messageProperties);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* 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,
|
||||
@@ -12,11 +12,9 @@ package org.springframework.amqp.support.converter;
|
||||
import static org.codehaus.jackson.map.type.TypeFactory.type;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.codehaus.jackson.JsonGenerationException;
|
||||
import org.codehaus.jackson.JsonParseException;
|
||||
import org.codehaus.jackson.map.DeserializationConfig;
|
||||
import org.codehaus.jackson.map.JsonMappingException;
|
||||
@@ -32,44 +30,21 @@ import org.springframework.amqp.core.MessageProperties;
|
||||
* @author James Carr
|
||||
* @author Dave Syer
|
||||
* @author Sam Nelson
|
||||
* @author Andreas Asplund
|
||||
*/
|
||||
public class JsonMessageConverter extends AbstractMessageConverter {
|
||||
public class JsonMessageConverter extends AbstractJsonMessageConverter {
|
||||
|
||||
private static Log log = LogFactory.getLog(JsonMessageConverter.class);
|
||||
|
||||
public static final String DEFAULT_CHARSET = "UTF-8";
|
||||
|
||||
private volatile String defaultCharset = DEFAULT_CHARSET;
|
||||
|
||||
private ObjectMapper jsonObjectMapper = new ObjectMapper();
|
||||
|
||||
private JavaTypeMapper javaTypeMapper = new DefaultJavaTypeMapper();
|
||||
|
||||
private ClassMapper classMapper = null;
|
||||
|
||||
public ClassMapper getClassMapper() {
|
||||
return classMapper;
|
||||
|
||||
}
|
||||
|
||||
public void setClassMapper(ClassMapper classMapper) {
|
||||
this.classMapper = classMapper;
|
||||
}
|
||||
|
||||
public JsonMessageConverter() {
|
||||
super();
|
||||
initializeJsonObjectMapper();
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the default charset to use when converting to or from text-based
|
||||
* Message body content. If not specified, the charset will be "UTF-8".
|
||||
*/
|
||||
public void setDefaultCharset(String defaultCharset) {
|
||||
this.defaultCharset = (defaultCharset != null) ? defaultCharset
|
||||
: DEFAULT_CHARSET;
|
||||
}
|
||||
|
||||
public JavaTypeMapper getJavaTypeMapper() {
|
||||
return javaTypeMapper;
|
||||
}
|
||||
@@ -110,7 +85,7 @@ public class JsonMessageConverter extends AbstractMessageConverter {
|
||||
if (contentType != null && contentType.contains("json")) {
|
||||
String encoding = properties.getContentEncoding();
|
||||
if (encoding == null) {
|
||||
encoding = this.defaultCharset;
|
||||
encoding = getDefaultCharset();
|
||||
}
|
||||
try {
|
||||
|
||||
@@ -119,26 +94,20 @@ public class JsonMessageConverter extends AbstractMessageConverter {
|
||||
.toJavaType(message.getMessageProperties());
|
||||
content = convertBytesToObject(message.getBody(),
|
||||
encoding, targetJavaType);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
Class<?> targetClass = getClassMapper().toClass(
|
||||
message.getMessageProperties());
|
||||
content = convertBytesToObject(message.getBody(),
|
||||
encoding, targetClass);
|
||||
}
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new MessageConversionException(
|
||||
"Failed to convert json-based Message content", e);
|
||||
} catch (JsonParseException e) {
|
||||
throw new MessageConversionException(
|
||||
"Failed to convert Message content", e);
|
||||
} catch (JsonMappingException e) {
|
||||
throw new MessageConversionException(
|
||||
"Failed to convert Message content", e);
|
||||
} catch (IOException e) {
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new MessageConversionException(
|
||||
"Failed to convert Message content", e);
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
log.warn("Could not convert incoming message with content-type ["
|
||||
+ contentType + "]");
|
||||
}
|
||||
@@ -171,22 +140,14 @@ public class JsonMessageConverter extends AbstractMessageConverter {
|
||||
try {
|
||||
String jsonString = jsonObjectMapper
|
||||
.writeValueAsString(objectToConvert);
|
||||
bytes = jsonString.getBytes(this.defaultCharset);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new MessageConversionException(
|
||||
"Failed to convert Message content", e);
|
||||
} catch (JsonGenerationException e) {
|
||||
throw new MessageConversionException(
|
||||
"Failed to convert Message content", e);
|
||||
} catch (JsonMappingException e) {
|
||||
throw new MessageConversionException(
|
||||
"Failed to convert Message content", e);
|
||||
} catch (IOException e) {
|
||||
bytes = jsonString.getBytes(getDefaultCharset());
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new MessageConversionException(
|
||||
"Failed to convert Message content", e);
|
||||
}
|
||||
messageProperties.setContentType(MessageProperties.CONTENT_TYPE_JSON);
|
||||
messageProperties.setContentEncoding(this.defaultCharset);
|
||||
messageProperties.setContentEncoding(getDefaultCharset());
|
||||
if (bytes != null) {
|
||||
messageProperties.setContentLength(bytes.length);
|
||||
}
|
||||
@@ -195,7 +156,8 @@ public class JsonMessageConverter extends AbstractMessageConverter {
|
||||
getJavaTypeMapper().fromJavaType(type(objectToConvert.getClass()),
|
||||
messageProperties);
|
||||
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
getClassMapper().fromClass(objectToConvert.getClass(),
|
||||
messageProperties);
|
||||
|
||||
|
||||
@@ -0,0 +1,238 @@
|
||||
/*
|
||||
* 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.amqp.support.converter;
|
||||
|
||||
import com.fasterxml.jackson.databind.JavaType;
|
||||
import com.fasterxml.jackson.databind.type.CollectionType;
|
||||
import com.fasterxml.jackson.databind.type.MapType;
|
||||
import com.fasterxml.jackson.databind.type.TypeFactory;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.fail;
|
||||
import org.junit.Test;
|
||||
import static org.junit.matchers.JUnitMatchers.containsString;
|
||||
import org.junit.runner.RunWith;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import org.mockito.Spy;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.springframework.amqp.core.MessageProperties;
|
||||
|
||||
/**
|
||||
* @author James Carr
|
||||
* @author Sam Nelson
|
||||
* @author Andreas Asplund
|
||||
*/
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class DefaultJackson2JavaTypeMapperTest {
|
||||
@Spy
|
||||
DefaultJackson2JavaTypeMapper javaTypeMapper = new DefaultJackson2JavaTypeMapper();
|
||||
private final MessageProperties properties = new MessageProperties();
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
private Class<ArrayList> containerClass = ArrayList.class;
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
private Class<HashMap> mapClass = HashMap.class;
|
||||
|
||||
@Test
|
||||
public void shouldThrowAnExceptionWhenClassIdNotPresent() {
|
||||
try {
|
||||
javaTypeMapper.toJavaType(properties);
|
||||
}
|
||||
catch (MessageConversionException e) {
|
||||
String classIdFieldName = javaTypeMapper.getClassIdFieldName();
|
||||
assertThat(e.getMessage(), containsString("Could not resolve " + classIdFieldName + " in header"));
|
||||
return;
|
||||
}
|
||||
fail();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldLookInTheClassIdFieldNameToFindTheClassName() {
|
||||
properties.getHeaders().put("type", "java.lang.String");
|
||||
given(javaTypeMapper.getClassIdFieldName()).willReturn("type");
|
||||
|
||||
JavaType javaType = javaTypeMapper.toJavaType(properties);
|
||||
|
||||
assertThat(javaType, equalTo(TypeFactory.defaultInstance().constructType(String.class)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldUseTheClassProvidedByTheLookupMapIfPresent() {
|
||||
properties.getHeaders().put("__TypeId__", "trade");
|
||||
javaTypeMapper.setIdClassMapping(map("trade", SimpleTrade.class));
|
||||
|
||||
JavaType javaType = javaTypeMapper.toJavaType(properties);
|
||||
|
||||
assertEquals(javaType, TypeFactory.defaultInstance().constructType(SimpleTrade.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fromJavaTypeShouldPopulateWithJavaTypeNameByDefault() {
|
||||
javaTypeMapper.fromJavaType(TypeFactory.defaultInstance().constructType(SimpleTrade.class), properties);
|
||||
|
||||
String className = (String) properties.getHeaders().get(javaTypeMapper.getClassIdFieldName());
|
||||
assertThat(className, equalTo(SimpleTrade.class.getName()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldUseSpecialNameForClassIfPresent() throws Exception {
|
||||
javaTypeMapper.setIdClassMapping(map("daytrade", SimpleTrade.class));
|
||||
javaTypeMapper.afterPropertiesSet();
|
||||
|
||||
javaTypeMapper.fromJavaType(TypeFactory.defaultInstance().constructType(SimpleTrade.class), properties);
|
||||
|
||||
String className = (String) properties.getHeaders().get(javaTypeMapper.getClassIdFieldName());
|
||||
assertThat(className, equalTo("daytrade"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldThrowAnExceptionWhenContentClassIdIsNotPresentWhenClassIdIsContainerType() {
|
||||
properties.getHeaders().put(javaTypeMapper.getClassIdFieldName(), ArrayList.class.getName());
|
||||
|
||||
try {
|
||||
javaTypeMapper.toJavaType(properties);
|
||||
}
|
||||
catch (MessageConversionException e) {
|
||||
String contentClassIdFieldName = javaTypeMapper.getContentClassIdFieldName();
|
||||
assertThat(e.getMessage(), containsString("Could not resolve " + contentClassIdFieldName + " in header"));
|
||||
return;
|
||||
}
|
||||
fail();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldLookInTheContentClassIdFieldNameToFindTheContainerClassIDWhenClassIdIsContainerType() {
|
||||
properties.getHeaders().put("contentType", "java.lang.String");
|
||||
properties.getHeaders().put(javaTypeMapper.getClassIdFieldName(), ArrayList.class.getName());
|
||||
given(javaTypeMapper.getContentClassIdFieldName()).willReturn("contentType");
|
||||
|
||||
JavaType javaType = javaTypeMapper.toJavaType(properties);
|
||||
|
||||
assertThat((CollectionType)javaType, equalTo(TypeFactory.defaultInstance().constructCollectionType(ArrayList.class, String.class)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldUseTheContentClassProvidedByTheLookupMapIfPresent() {
|
||||
|
||||
properties.getHeaders().put(javaTypeMapper.getClassIdFieldName(), containerClass.getName());
|
||||
properties.getHeaders().put("__ContentTypeId__", "trade");
|
||||
|
||||
Map<String, Class<?>> map = map("trade", SimpleTrade.class);
|
||||
map.put(javaTypeMapper.getClassIdFieldName(), containerClass);
|
||||
javaTypeMapper.setIdClassMapping(map);
|
||||
|
||||
JavaType javaType = javaTypeMapper.toJavaType(properties);
|
||||
|
||||
assertThat((CollectionType)javaType, equalTo(TypeFactory.defaultInstance().constructCollectionType(containerClass, TypeFactory.defaultInstance().constructType(SimpleTrade.class))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fromJavaTypeShouldPopulateWithContentTypeJavaTypeNameByDefault() {
|
||||
|
||||
javaTypeMapper.fromJavaType(TypeFactory.defaultInstance().constructCollectionType(containerClass, TypeFactory.defaultInstance().constructType(SimpleTrade.class)),
|
||||
properties);
|
||||
|
||||
String className = (String) properties.getHeaders().get(javaTypeMapper.getClassIdFieldName());
|
||||
String contentClassName = (String) properties.getHeaders().get(javaTypeMapper.getContentClassIdFieldName());
|
||||
|
||||
assertThat(className, equalTo(ArrayList.class.getName()));
|
||||
assertThat(contentClassName, equalTo(SimpleTrade.class.getName()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldThrowAnExceptionWhenKeyClassIdIsNotPresentWhenClassIdIsAMap() {
|
||||
properties.getHeaders().put(javaTypeMapper.getClassIdFieldName(), HashMap.class.getName());
|
||||
properties.getHeaders().put(javaTypeMapper.getKeyClassIdFieldName(), String.class.getName());
|
||||
|
||||
try {
|
||||
javaTypeMapper.toJavaType(properties);
|
||||
}
|
||||
catch (MessageConversionException e) {
|
||||
String contentClassIdFieldName = javaTypeMapper.getContentClassIdFieldName();
|
||||
assertThat(e.getMessage(), containsString("Could not resolve " + contentClassIdFieldName + " in header"));
|
||||
return;
|
||||
}
|
||||
fail();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldLookInTheValueClassIdFieldNameToFindTheValueClassIDWhenClassIdIsAMap() {
|
||||
properties.getHeaders().put("keyType", "java.lang.Integer");
|
||||
properties.getHeaders().put(javaTypeMapper.getContentClassIdFieldName(), "java.lang.String");
|
||||
properties.getHeaders().put(javaTypeMapper.getClassIdFieldName(), HashMap.class.getName());
|
||||
given(javaTypeMapper.getKeyClassIdFieldName()).willReturn("keyType");
|
||||
|
||||
JavaType javaType = javaTypeMapper.toJavaType(properties);
|
||||
|
||||
assertThat((MapType)javaType, equalTo(TypeFactory.defaultInstance().constructMapType(HashMap.class, Integer.class, String.class)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldUseTheKeyClassProvidedByTheLookupMapIfPresent() {
|
||||
properties.getHeaders().put(javaTypeMapper.getClassIdFieldName(), mapClass.getName());
|
||||
properties.getHeaders().put(javaTypeMapper.getContentClassIdFieldName(), "java.lang.String");
|
||||
properties.getHeaders().put("__KeyTypeId__", "trade");
|
||||
|
||||
Map<String, Class<?>> map = map("trade", SimpleTrade.class);
|
||||
map.put(javaTypeMapper.getClassIdFieldName(), mapClass);
|
||||
map.put(javaTypeMapper.getContentClassIdFieldName(), String.class);
|
||||
javaTypeMapper.setIdClassMapping(map);
|
||||
|
||||
JavaType javaType = javaTypeMapper.toJavaType(properties);
|
||||
|
||||
assertThat((MapType)javaType,
|
||||
equalTo(TypeFactory.defaultInstance().constructMapType(mapClass, TypeFactory.defaultInstance().constructType(SimpleTrade.class),
|
||||
TypeFactory.defaultInstance().constructType(String.class))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fromJavaTypeShouldPopulateWithKeyTypeAndContentJavaTypeNameByDefault() {
|
||||
|
||||
javaTypeMapper.fromJavaType(TypeFactory.defaultInstance().constructMapType(mapClass, TypeFactory.defaultInstance().constructType(SimpleTrade.class),
|
||||
TypeFactory.defaultInstance().constructType(String.class)), properties);
|
||||
|
||||
String className = (String) properties.getHeaders().get(javaTypeMapper.getClassIdFieldName());
|
||||
String contentClassName = (String) properties.getHeaders().get(javaTypeMapper.getContentClassIdFieldName());
|
||||
String keyClassName = (String) properties.getHeaders().get(javaTypeMapper.getKeyClassIdFieldName());
|
||||
|
||||
assertThat(className, equalTo(HashMap.class.getName()));
|
||||
assertThat(contentClassName, equalTo(String.class.getName()));
|
||||
assertThat(keyClassName, equalTo(SimpleTrade.class.getName()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fromClassShouldPopulateWithJavaTypeNameByDefault() {
|
||||
javaTypeMapper.fromClass(SimpleTrade.class, properties);
|
||||
|
||||
String className = (String) properties.getHeaders().get(javaTypeMapper.getClassIdFieldName());
|
||||
assertThat(className, equalTo(SimpleTrade.class.getName()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toClassShouldUseTheClassProvidedByTheLookupMapIfPresent() {
|
||||
properties.getHeaders().put("__TypeId__", "trade");
|
||||
javaTypeMapper.setIdClassMapping(map("trade", SimpleTrade.class));
|
||||
|
||||
Class<?> clazz = javaTypeMapper.toClass(properties);
|
||||
|
||||
assertEquals(SimpleTrade.class, clazz);
|
||||
}
|
||||
|
||||
private Map<String, Class<?>> map(String string, Class<?> clazz) {
|
||||
Map<String, Class<?>> map = new HashMap<String, Class<?>>();
|
||||
map.put(string, clazz);
|
||||
return map;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,264 @@
|
||||
/*
|
||||
* 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.amqp.support.converter;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Hashtable;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.amqp.core.Message;
|
||||
import org.springframework.amqp.core.MessageProperties;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.ser.BeanSerializerFactory;
|
||||
|
||||
/**
|
||||
* @author Mark Pollack
|
||||
* @author Dave Syer
|
||||
* @author Sam Nelson
|
||||
* @author Gary Russell
|
||||
* @author Andreas Asplund
|
||||
*/
|
||||
@ContextConfiguration
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
public class Jackson2JsonMessageConverterTests {
|
||||
|
||||
private Jackson2JsonMessageConverter converter;
|
||||
private SimpleTrade trade;
|
||||
|
||||
@Autowired
|
||||
private Jackson2JsonMessageConverter jsonConverterWithDefaultType;
|
||||
|
||||
@Before
|
||||
public void before(){
|
||||
converter = new Jackson2JsonMessageConverter();
|
||||
trade = new SimpleTrade();
|
||||
trade.setAccountName("Acct1");
|
||||
trade.setBuyRequest(true);
|
||||
trade.setOrderType("Market");
|
||||
trade.setPrice(new BigDecimal(103.30));
|
||||
trade.setQuantity(100);
|
||||
trade.setRequestId("R123");
|
||||
trade.setTicker("VMW");
|
||||
trade.setUserName("Joe Trader");
|
||||
|
||||
}
|
||||
@Test
|
||||
public void simpleTrade() {
|
||||
Message message = converter.toMessage(trade, new MessageProperties());
|
||||
|
||||
SimpleTrade marshalledTrade = (SimpleTrade) converter.fromMessage(message);
|
||||
assertEquals(trade, marshalledTrade);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void simpleTradeOverrideMapper() {
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
mapper.setSerializerFactory(BeanSerializerFactory.instance);
|
||||
converter.setJsonObjectMapper(mapper);
|
||||
|
||||
Message message = converter.toMessage(trade, new MessageProperties());
|
||||
|
||||
SimpleTrade marshalledTrade = (SimpleTrade) converter.fromMessage(message);
|
||||
assertEquals(trade, marshalledTrade);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nestedBean() {
|
||||
Bar bar = new Bar();
|
||||
bar.getFoo().setName("spam");
|
||||
|
||||
Message message = converter.toMessage(bar, new MessageProperties());
|
||||
|
||||
Bar marshalled = (Bar) converter.fromMessage(message);
|
||||
assertEquals(bar, marshalled);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void hashtable() {
|
||||
Hashtable<String, String> hashtable = new Hashtable<String, String>();
|
||||
hashtable.put("TICKER", "VMW");
|
||||
hashtable.put("PRICE", "103.2");
|
||||
|
||||
Message message = converter.toMessage(hashtable, new MessageProperties());
|
||||
Hashtable<String, String> marhsalledHashtable = (Hashtable<String, String>) converter.fromMessage(message);
|
||||
|
||||
assertEquals("VMW", marhsalledHashtable.get("TICKER"));
|
||||
assertEquals("103.2", marhsalledHashtable.get("PRICE"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldUseClassMapperWhenProvided() {
|
||||
Message message = converter.toMessage(trade, new MessageProperties());
|
||||
|
||||
converter.setClassMapper(new DefaultClassMapper());
|
||||
converter.setJavaTypeMapper(null);
|
||||
|
||||
SimpleTrade marshalledTrade = (SimpleTrade) converter.fromMessage(message);
|
||||
assertEquals(trade, marshalledTrade);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldUseClassMapperWhenProvidedOutbound() {
|
||||
converter.setClassMapper(new DefaultClassMapper());
|
||||
converter.setJavaTypeMapper(null);
|
||||
Message message = converter.toMessage(trade, new MessageProperties());
|
||||
|
||||
SimpleTrade marshalledTrade = (SimpleTrade) converter.fromMessage(message);
|
||||
assertEquals(trade, marshalledTrade);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDefaultType() {
|
||||
byte[] bytes = "{\"name\" : \"foo\" }".getBytes();
|
||||
MessageProperties messageProperties = new MessageProperties();
|
||||
messageProperties.setContentType("application/json");
|
||||
Message message = new Message(bytes, messageProperties);
|
||||
JsonMessageConverter converter = new JsonMessageConverter();
|
||||
DefaultClassMapper classMapper = new DefaultClassMapper();
|
||||
classMapper.setDefaultType(Foo.class);
|
||||
converter.setClassMapper(classMapper);
|
||||
Object foo = converter.fromMessage(message);
|
||||
assertTrue(foo instanceof Foo);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDefaultTypeConfig() {
|
||||
byte[] bytes = "{\"name\" : \"foo\" }".getBytes();
|
||||
MessageProperties messageProperties = new MessageProperties();
|
||||
messageProperties.setContentType("application/json");
|
||||
Message message = new Message(bytes, messageProperties);
|
||||
Object foo = jsonConverterWithDefaultType.fromMessage(message);
|
||||
assertTrue(foo instanceof Foo);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNoJsonContentType() {
|
||||
byte[] bytes = "{\"name\" : \"foo\" }".getBytes();
|
||||
MessageProperties messageProperties = new MessageProperties();
|
||||
Message message = new Message(bytes, messageProperties);
|
||||
Object foo = jsonConverterWithDefaultType.fromMessage(message);
|
||||
assertEquals(new String(bytes), new String((byte[]) foo));
|
||||
}
|
||||
|
||||
public static class Foo {
|
||||
private String name = "foo";
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((name == null) ? 0 : name.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
Foo other = (Foo) obj;
|
||||
if (name == null) {
|
||||
if (other.name != null) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (!name.equals(other.name)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Bar {
|
||||
private String name = "bar";
|
||||
private Foo foo = new Foo();
|
||||
|
||||
public Foo getFoo() {
|
||||
return foo;
|
||||
}
|
||||
|
||||
public void setFoo(Foo foo) {
|
||||
this.foo = foo;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((foo == null) ? 0 : foo.hashCode());
|
||||
result = prime * result + ((name == null) ? 0 : name.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
Bar other = (Bar) obj;
|
||||
if (foo == null) {
|
||||
if (other.foo != null) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (!foo.equals(other.foo)) {
|
||||
return false;
|
||||
}
|
||||
if (name == null) {
|
||||
if (other.name != null) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (!name.equals(other.name)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* 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,
|
||||
@@ -112,6 +112,16 @@ public class JsonMessageConverterTests {
|
||||
assertEquals(trade, marshalledTrade);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldUseClassMapperWhenProvidedOutbound() {
|
||||
converter.setClassMapper(new DefaultClassMapper());
|
||||
converter.setJavaTypeMapper(null);
|
||||
Message message = converter.toMessage(trade, new MessageProperties());
|
||||
|
||||
SimpleTrade marshalledTrade = (SimpleTrade) converter.fromMessage(message);
|
||||
assertEquals(trade, marshalledTrade);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDefaultType() {
|
||||
byte[] bytes = "{\"name\" : \"foo\" }".getBytes();
|
||||
@@ -136,6 +146,15 @@ public class JsonMessageConverterTests {
|
||||
assertTrue(foo instanceof Foo);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNoJsonContentType() {
|
||||
byte[] bytes = "{\"name\" : \"foo\" }".getBytes();
|
||||
MessageProperties messageProperties = new MessageProperties();
|
||||
Message message = new Message(bytes, messageProperties);
|
||||
Object foo = jsonConverterWithDefaultType.fromMessage(message);
|
||||
assertEquals(new String(bytes), new String((byte[]) foo));
|
||||
}
|
||||
|
||||
public static class Foo {
|
||||
private String name = "foo";
|
||||
|
||||
@@ -157,14 +176,24 @@ public class JsonMessageConverterTests {
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) return true;
|
||||
if (obj == null) return false;
|
||||
if (getClass() != obj.getClass()) return false;
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
Foo other = (Foo) obj;
|
||||
if (name == null) {
|
||||
if (other.name != null) return false;
|
||||
if (other.name != null) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (!name.equals(other.name)) return false;
|
||||
else if (!name.equals(other.name)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -200,18 +229,32 @@ public class JsonMessageConverterTests {
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) return true;
|
||||
if (obj == null) return false;
|
||||
if (getClass() != obj.getClass()) return false;
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
Bar other = (Bar) obj;
|
||||
if (foo == null) {
|
||||
if (other.foo != null) return false;
|
||||
if (other.foo != null) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (!foo.equals(other.foo)) return false;
|
||||
else if (!foo.equals(other.foo)) {
|
||||
return false;
|
||||
}
|
||||
if (name == null) {
|
||||
if (other.name != null) return false;
|
||||
if (other.name != null) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (!name.equals(other.name)) return false;
|
||||
else if (!name.equals(other.name)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||
|
||||
<bean id="jsonConverterWithDefaultType" class="org.springframework.amqp.support.converter.Jackson2JsonMessageConverter">
|
||||
<property name="classMapper">
|
||||
<bean class="org.springframework.amqp.support.converter.DefaultClassMapper">
|
||||
<property name="defaultType"
|
||||
value="org.springframework.amqp.support.converter.Jackson2JsonMessageConverterTests$Foo" />
|
||||
</bean>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
</beans>
|
||||
@@ -533,7 +533,7 @@ Object receiveAndConvert(String queueName) throws AmqpException;]]></programlist
|
||||
</para>
|
||||
</sect2>
|
||||
|
||||
<sect2><title>Asynchronous Consumer</title><para>For asynchronous Message
|
||||
<sect2 id="async-consumer"><title>Asynchronous Consumer</title><para>For asynchronous Message
|
||||
reception, a dedicated component (not the
|
||||
<interfacename>AmqpTemplate</interfacename>) is involved. That component
|
||||
is a container for a Message consuming callback. We will look at the
|
||||
@@ -666,7 +666,10 @@ void convertAndSend(String exchange, String routingKey, Object message,
|
||||
<programlisting language="java"><![CDATA[Object receiveAndConvert() throws AmqpException;
|
||||
|
||||
Object receiveAndConvert(String queueName) throws AmqpException;]]></programlisting>
|
||||
|
||||
<note>
|
||||
The <classname>MessageListenerAdapter</classname> mentioned in <xref linkend="async-consumer"/>
|
||||
also uses a <classname>MessageConverter</classname>.
|
||||
</note>
|
||||
<sect2>
|
||||
<title>SimpleMessageConverter</title>
|
||||
|
||||
@@ -723,15 +726,20 @@ Object receiveAndConvert(String queueName) throws AmqpException;]]></programlist
|
||||
</sect2>
|
||||
|
||||
<sect2>
|
||||
<title>JsonMessageConverter</title>
|
||||
<title>JsonMessageConverter and Jackson2JsonMessageConverter</title>
|
||||
|
||||
<para>As mentioned in the previous section, relying on Java
|
||||
serialization is generally not recommended. One rather common
|
||||
alternative that is more flexible and portable across different
|
||||
languages and platforms is JSON (JavaScript Object Notation). An
|
||||
implementation is available and can be configured on any
|
||||
languages and platforms is JSON (JavaScript Object Notation). Two
|
||||
implementations are available and can be configured on any
|
||||
<classname>RabbitTemplate</classname> instance to override its usage of
|
||||
the <classname>SimpleMessageConverter</classname> default.</para>
|
||||
the <classname>SimpleMessageConverter</classname> default.
|
||||
The <classname>JsonMessageConverter</classname> which uses the
|
||||
<code>org.codehaus.jackson</code> 1.x library
|
||||
and <classname>Jackson2JsonMessageConverter</classname> which uses the
|
||||
<code>com.fasterxml.jackson</code> 2.x library.
|
||||
</para>
|
||||
|
||||
<programlisting language="xml"><![CDATA[<bean class="org.springframework.amqp.rabbit.core.RabbitTemplate">
|
||||
<property name="connectionFactory" ref="rabbitConnectionFactory"/>
|
||||
@@ -743,7 +751,16 @@ Object receiveAndConvert(String queueName) throws AmqpException;]]></programlist
|
||||
</property>
|
||||
</bean>]]></programlisting>
|
||||
|
||||
<para>As shown above, the <classname>JsonMessageConverter</classname> uses a
|
||||
<programlisting language="xml"><![CDATA[<bean class="org.springframework.amqp.rabbit.core.RabbitTemplate">
|
||||
<property name="connectionFactory" ref="rabbitConnectionFactory"/>
|
||||
<property name="messageConverter">
|
||||
<bean class="org.springframework.amqp.support.converter.Jackson2JsonMessageConverter">
|
||||
<!-- if necessary, override the DefaultClassMapper -->
|
||||
<property name="classMapper" ref="customClassMapper"/>
|
||||
</bean>
|
||||
</property>
|
||||
</bean>]]></programlisting>
|
||||
<para>As shown above, the <classname>JsonMessageConverter</classname> and <classname>Jackson2JsonMessageConverter</classname> uses a
|
||||
<classname>DefaultClassMapper</classname> by default. Type information is
|
||||
added to (and retrieved from) the <classname>MessageProperties</classname>.
|
||||
If an inbound message does not contain type information in the
|
||||
@@ -758,6 +775,15 @@ Object receiveAndConvert(String queueName) throws AmqpException;]]></programlist
|
||||
</bean>
|
||||
</property>
|
||||
</bean>]]></programlisting>
|
||||
|
||||
<programlisting language="xml"><![CDATA[<bean id="jsonConverterWithDefaultType" class="org.springframework.amqp.support.converter.Jackson2JsonMessageConverter">
|
||||
<property name="classMapper">
|
||||
<bean class="org.springframework.amqp.support.converter.DefaultClassMapper">
|
||||
<property name="defaultType"
|
||||
value="foo.PurchaseOrder" />
|
||||
</bean>
|
||||
</property>
|
||||
</bean>]]></programlisting>
|
||||
</sect2>
|
||||
|
||||
<sect2>
|
||||
|
||||
Reference in New Issue
Block a user