AMQP-277 Add DefaultType to DefaultClassMapper
Permit the use of a JsonMessageConverter when no type information is provided in the MessageProperties and the type is known.
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* 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
|
||||
@@ -22,21 +22,37 @@ import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
*
|
||||
* Maps to/from JSON using type information in the {@link MessageProperties};
|
||||
* the default name of the message property containing the type is '__TypeId__'.
|
||||
* An optional property {@link #setDefaultType(Class)}
|
||||
* is provided that allows mapping to a statically defined type, if no message property is
|
||||
* found in the message properties.
|
||||
* @author Mark Pollack
|
||||
*
|
||||
* @author Gary Russell
|
||||
*
|
||||
*/
|
||||
public class DefaultClassMapper implements ClassMapper, InitializingBean {
|
||||
|
||||
public static final String DEFAULT_CLASSID_FIELD_NAME = "__TypeId__";
|
||||
|
||||
private Map<String, Class<?>> idClassMapping = new HashMap<String, Class<?>>();
|
||||
private volatile Map<String, Class<?>> idClassMapping = new HashMap<String, Class<?>>();
|
||||
|
||||
private Map<Class<?>, String> classIdMapping = new HashMap<Class<?>, String>();
|
||||
private volatile Map<Class<?>, String> classIdMapping = new HashMap<Class<?>, String>();
|
||||
|
||||
private String defaultHashtableTypeId = "Hashtable";
|
||||
private final String defaultHashtableTypeId = "Hashtable";
|
||||
|
||||
private Class<?> defaultHashtableClass = Hashtable.class;
|
||||
private volatile Class<?> defaultHashtableClass = Hashtable.class;
|
||||
|
||||
private volatile Class<?> defaultType;
|
||||
|
||||
/**
|
||||
* The type returned by {@link #toClass(MessageProperties)} if no type information
|
||||
* is found in the message properties.
|
||||
* @param defaultType the defaultType to set
|
||||
*/
|
||||
public void setDefaultType(Class<?> defaultType) {
|
||||
this.defaultType = defaultType;
|
||||
}
|
||||
|
||||
public void setDefaultHashtableClass(Class<?> defaultHashtableClass) {
|
||||
this.defaultHashtableClass = defaultHashtableClass;
|
||||
@@ -105,9 +121,15 @@ public class DefaultClassMapper implements ClassMapper, InitializingBean {
|
||||
classId = classIdFieldNameValue.toString();
|
||||
}
|
||||
if (classId == null) {
|
||||
throw new MessageConversionException(
|
||||
"failed to convert Message content. Could not resolve "
|
||||
+ getClassIdFieldName() + " in header");
|
||||
if (this.defaultType != null) {
|
||||
return this.defaultType;
|
||||
}
|
||||
else {
|
||||
throw new MessageConversionException(
|
||||
"failed to convert Message content. Could not resolve "
|
||||
+ getClassIdFieldName() + " in header " +
|
||||
"and no defaultType provided");
|
||||
}
|
||||
}
|
||||
return toClass(classId);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* 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
|
||||
@@ -14,6 +14,7 @@ package org.springframework.amqp.support.converter;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.matchers.JUnitMatchers.containsString;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
@@ -28,15 +29,18 @@ import org.junit.runner.RunWith;
|
||||
import org.mockito.Spy;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.springframework.amqp.core.MessageProperties;
|
||||
import org.springframework.amqp.support.converter.JsonMessageConverterTests.Foo;
|
||||
|
||||
/**
|
||||
* @author James Carr
|
||||
*
|
||||
* @author Gary Russell
|
||||
*
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class DefaultClassMapperTest {
|
||||
@Spy
|
||||
DefaultClassMapper classMapper = new DefaultClassMapper();
|
||||
|
||||
private final MessageProperties props = new MessageProperties();
|
||||
|
||||
@Test
|
||||
@@ -115,6 +119,17 @@ public class DefaultClassMapperTest {
|
||||
assertThat(className, equalTo("Hashtable"));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test
|
||||
public void shouldUseDefaultType() {
|
||||
props.getHeaders().clear();
|
||||
classMapper.setDefaultType(Foo.class);
|
||||
Class<Foo> clazz = (Class<Foo>) classMapper.toClass(props);
|
||||
|
||||
assertSame(Foo.class, clazz);
|
||||
classMapper.setDefaultType(null);
|
||||
}
|
||||
|
||||
private Map<String, Class<?>> map(String string, Class<?> class1) {
|
||||
Map<String, Class<?>> map = new HashMap<String, Class<?>>();
|
||||
map.put(string, class1);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* 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,
|
||||
@@ -10,6 +10,7 @@
|
||||
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;
|
||||
@@ -18,19 +19,29 @@ import org.codehaus.jackson.map.ObjectMapper;
|
||||
import org.codehaus.jackson.map.ser.BeanSerializerFactory;
|
||||
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;
|
||||
|
||||
/**
|
||||
* @author Mark Pollack
|
||||
* @author Dave Syer
|
||||
* @author Sam Nelson
|
||||
* @author Gary Russell
|
||||
*/
|
||||
@ContextConfiguration
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
public class JsonMessageConverterTests {
|
||||
|
||||
private JsonMessageConverter converter;
|
||||
private SimpleTrade trade;
|
||||
|
||||
@Autowired
|
||||
private JsonMessageConverter jsonConverterWithDefaultType;
|
||||
|
||||
@Before
|
||||
public void before(){
|
||||
converter = new JsonMessageConverter();
|
||||
@@ -43,7 +54,7 @@ public class JsonMessageConverterTests {
|
||||
trade.setRequestId("R123");
|
||||
trade.setTicker("VMW");
|
||||
trade.setUserName("Joe Trader");
|
||||
|
||||
|
||||
}
|
||||
@Test
|
||||
public void simpleTrade() {
|
||||
@@ -58,7 +69,7 @@ public class JsonMessageConverterTests {
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
mapper.setSerializerFactory(BeanSerializerFactory.instance);
|
||||
converter.setJsonObjectMapper(mapper);
|
||||
|
||||
|
||||
Message message = converter.toMessage(trade, new MessageProperties());
|
||||
|
||||
SimpleTrade marshalledTrade = (SimpleTrade) converter.fromMessage(message);
|
||||
@@ -82,25 +93,49 @@ public class JsonMessageConverterTests {
|
||||
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 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);
|
||||
}
|
||||
|
||||
public static class Foo {
|
||||
private String name = "foo";
|
||||
|
||||
|
||||
@@ -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.JsonMessageConverter">
|
||||
<property name="classMapper">
|
||||
<bean class="org.springframework.amqp.support.converter.DefaultClassMapper">
|
||||
<property name="defaultType"
|
||||
value="org.springframework.amqp.support.converter.JsonMessageConverterTests$Foo" />
|
||||
</bean>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
</beans>
|
||||
@@ -742,6 +742,22 @@ Object receiveAndConvert(String queueName) throws AmqpException;]]></programlist
|
||||
</bean>
|
||||
</property>
|
||||
</bean>]]></programlisting>
|
||||
|
||||
<para>As shown above, the <classname>JsonMessageConverter</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
|
||||
<classname>MessageProperties</classname>, but you know the expected type,
|
||||
you can configure a static type using the <code>defaultType</code> property</para>
|
||||
|
||||
<programlisting language="xml"><![CDATA[<bean id="jsonConverterWithDefaultType" class="org.springframework.amqp.support.converter.JsonMessageConverter">
|
||||
<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