Skip to content
Prev Previous commit
Next Next commit
fixed for maps
  • Loading branch information
rkegg committed Nov 12, 2011
commit 9eafc1a45260dff1eef37aa4266f7930ac89333c
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,11 @@
import java.util.Map;
import java.util.Map.Entry;

import org.codehaus.jackson.map.type.CollectionType;
import org.codehaus.jackson.map.type.MapType;
import org.codehaus.jackson.map.type.TypeFactory;
import org.codehaus.jackson.type.JavaType;
import org.hamcrest.core.IsEqual;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.ClassUtils;
Expand All @@ -33,9 +36,23 @@ public class DefaultJavaTypeMapper implements JavaTypeMapper, 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;
}

@SuppressWarnings({ "unchecked", "rawtypes" })
public JavaType toJavaType(MessageProperties properties) {
JavaType classType = getClassIdType(retrieveHeader(properties,
Expand All @@ -44,14 +61,20 @@ public JavaType toJavaType(MessageProperties properties) {
return classType;
}

String contentClassId = retrieveHeader(properties,
getContentClassIdFieldName());
JavaType contentClassType = getClassIdType(contentClassId);
JavaType collectionType = TypeFactory.collectionType(
(Class<? extends Collection>) classType.getRawClass(),
JavaType contentClassType = getClassIdType(retrieveHeader(properties,
getContentClassIdFieldName()));
if (classType.getKeyType() == null) {
return TypeFactory.collectionType(
(Class<? extends Collection>) classType.getRawClass(),
contentClassType);
}

JavaType keyClassType = getClassIdType(retrieveHeader(properties, getKeyClassIdFieldName()));
JavaType mapType = TypeFactory.mapType(
(Class<? extends Map>) classType.getRawClass(), keyClassType,
contentClassType);
return collectionType;

return mapType;
}

private JavaType getClassIdType(String classId) {
Expand Down Expand Up @@ -89,10 +112,6 @@ private String retrieveHeader(MessageProperties properties,
return classId;
}

public String getClassIdFieldName() {
return DEFAULT_CLASSID_FIELD_NAME;
}

public void setIdClassMapping(Map<String, Class<?>> idClassMapping) {
this.idClassMapping = idClassMapping;
}
Expand All @@ -105,8 +124,18 @@ public void fromJavaType(JavaType javaType, MessageProperties properties) {
addHeader(properties, getContentClassIdFieldName(), javaType
.getContentType().getRawClass());
}

if (javaType.getKeyType() != null) {
addHeader(properties, getKeyClassIdFieldName(), javaType
.getKeyType().getRawClass());
}
}

public void afterPropertiesSet() throws Exception {
validateIdTypeMapping();
}


private void addHeader(MessageProperties properties, String headerName,
Class<?> clazz) {
if (classIdMapping.containsKey(clazz)) {
Expand All @@ -116,10 +145,7 @@ private void addHeader(MessageProperties properties, String headerName,
}
}

public void afterPropertiesSet() throws Exception {
validateIdTypeMapping();
}


private void validateIdTypeMapping() {
Map<String, Class<?>> finalIdClassMapping = new HashMap<String, Class<?>>();
for (Entry<String, Class<?>> entry : idClassMapping.entrySet()) {
Expand All @@ -130,9 +156,4 @@ private void validateIdTypeMapping() {
}
this.idClassMapping = finalIdClassMapping;
}

public String getContentClassIdFieldName() {
return DEFAULT_CONTENT_CLASSID_FIELD_NAME;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
import org.codehaus.jackson.map.DeserializationConfig;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.type.TypeFactory;
import org.codehaus.jackson.type.JavaType;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageProperties;

Expand All @@ -32,6 +34,7 @@
* @author Mark Pollack
* @author James Carr
* @author Dave Syer
* @author Sam Nelson
*/
public class JsonMessageConverter extends AbstractMessageConverter {

Expand All @@ -43,7 +46,8 @@ public class JsonMessageConverter extends AbstractMessageConverter {

private ObjectMapper jsonObjectMapper = new ObjectMapper();

private ClassMapper classMapper = new DefaultClassMapper();
private JavaTypeMapper javaTypeMapper = new DefaultJavaTypeMapper();


public JsonMessageConverter() {
super();
Expand All @@ -58,12 +62,12 @@ public void setDefaultCharset(String defaultCharset) {
this.defaultCharset = (defaultCharset != null) ? defaultCharset : DEFAULT_CHARSET;
}

public ClassMapper getClassMapper() {
return classMapper;
public JavaTypeMapper getJavaTypeMapper() {
return javaTypeMapper;
}

public void setClassMapper(ClassMapper classMapper) {
this.classMapper = classMapper;
public void setJavaTypeMapper(JavaTypeMapper javaTypeMapper) {
this.javaTypeMapper = javaTypeMapper;
}

/**
Expand Down Expand Up @@ -94,9 +98,7 @@ public Object fromMessage(Message message) throws MessageConversionException {
encoding = this.defaultCharset;
}
try {
// content = new String(message.getBody(), encoding);

Class<?> targetClass = classMapper.toClass(message.getMessageProperties());
JavaType targetClass = javaTypeMapper.toJavaType(message.getMessageProperties());
content = convertBytesToObject(message.getBody(), encoding, targetClass);
} catch (UnsupportedEncodingException e) {
throw new MessageConversionException("Failed to convert json-based Message content", e);
Expand All @@ -117,10 +119,10 @@ public Object fromMessage(Message message) throws MessageConversionException {
return content;
}

private Object convertBytesToObject(byte[] body, String encoding, Class<?> targetClass) throws JsonParseException,
private Object convertBytesToObject(byte[] body, String encoding, JavaType targetJavaType) throws JsonParseException,
JsonMappingException, IOException {
String contentAsString = new String(body, encoding);
return jsonObjectMapper.readValue(contentAsString, targetClass);
return jsonObjectMapper.readValue(contentAsString, targetJavaType);
}

protected Message createMessage(Object objectToConvert, MessageProperties messageProperties)
Expand All @@ -143,7 +145,7 @@ protected Message createMessage(Object objectToConvert, MessageProperties messag
if (bytes != null) {
messageProperties.setContentLength(bytes.length);
}
classMapper.fromClass(objectToConvert.getClass(), messageProperties);
javaTypeMapper.fromJavaType(TypeFactory.type(objectToConvert.getClass()), messageProperties);
return new Message(bytes, messageProperties);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

import org.codehaus.jackson.map.type.TypeFactory;
import org.codehaus.jackson.type.JavaType;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Spy;
Expand All @@ -45,6 +46,9 @@ public class DefaultJavaTypeMapperTest {
@SuppressWarnings("rawtypes")
private Class<ArrayList> containerClass = ArrayList.class;

@SuppressWarnings("rawtypes")
private Class<HashMap> mapClass = HashMap.class;

@Test
public void shouldThrowAnExceptionWhenClassIdNotPresent(){
try{
Expand Down Expand Up @@ -150,6 +154,64 @@ public void fromJavaTypeShouldPopulateWithContentTypeJavaTypeNameByDefault(){
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(javaType, equalTo(TypeFactory.mapType(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(javaType, equalTo(TypeFactory.mapType(mapClass, TypeFactory.type(SimpleTrade.class), TypeFactory.type(String.class))));
}

@Test
public void fromJavaTypeShouldPopulateWithKeyTypeAndContentJavaTypeNameByDefault(){

javaTypeMapper.fromJavaType(TypeFactory.mapType(mapClass, TypeFactory.type(SimpleTrade.class), TypeFactory.type(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()));
}

private Map<String, Class<?>> map(String string,Class<?> clazz) {
Map<String, Class<?>> map = new HashMap<String, Class<?>>();
map.put(string, clazz);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ public void hashtable() {
hashtable.put("TICKER", "VMW");
hashtable.put("PRICE", "103.2");
JsonMessageConverter converter = new JsonMessageConverter();
Message message = converter.toMessage(hashtable, new MessageProperties());
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"));
Expand Down