Merge pull request #761 from garyrussell/INT-2955

* INT-2955: Fix Collection/Map Conversion
This commit is contained in:
Mark Fisher
2013-03-27 17:31:06 -04:00
2 changed files with 168 additions and 5 deletions

View File

@@ -27,6 +27,9 @@ import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.expression.TypeConverter;
import org.springframework.integration.MessageHeaders;
import org.springframework.integration.history.MessageHistory;
import org.springframework.util.ClassUtils;
/**
* @author Dave Syer
@@ -106,10 +109,15 @@ public class BeanFactoryTypeConverter implements TypeConverter, BeanFactoryAware
* INT-2630 Spring 3.1 now converts ALL arguments; we know we don't need to convert MessageHeaders
* or MessageHistory; the MapToMap converter requires a no-arg constructor.
* Also INT-2650 - don't convert large byte[]
* This reverts the effective logic to Spring 3.0.
*/
if (sourceType != null && sourceType.isAssignableTo(targetType)) {
return value;
if (sourceType != null) {
Class<?> sourceClass = sourceType.getType();
Class<?> targetClass = targetType.getType();
if ((sourceClass == MessageHeaders.class && targetClass == MessageHeaders.class) ||
(sourceClass == MessageHistory.class && targetClass == MessageHistory.class) ||
(sourceType.isAssignableTo(targetType) && ClassUtils.isPrimitiveArray(sourceClass))) {
return value;
}
}
if (conversionService.canConvert(sourceType, targetType)) {
return conversionService.convert(value, sourceType, targetType);
@@ -125,7 +133,7 @@ public class BeanFactoryTypeConverter implements TypeConverter, BeanFactoryAware
editor.setValue(value);
text = editor.getAsText();
}
if (String.class.isAssignableFrom(targetType.getClass())) {
if (String.class.isAssignableFrom(targetType.getType())) {
return text;
}
return convertValue(text, TypeDescriptor.valueOf(String.class), targetType);

View File

@@ -17,6 +17,8 @@ package org.springframework.integration.util;
import static junit.framework.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
@@ -28,7 +30,12 @@ import static org.mockito.Mockito.when;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@@ -44,9 +51,14 @@ import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.integration.Message;
import org.springframework.integration.MessageHeaders;
import org.springframework.integration.channel.QueueChannel;
import org.springframework.integration.context.NamedComponent;
import org.springframework.integration.handler.MethodInvokingMessageProcessor;
import org.springframework.integration.handler.ServiceActivatingHandler;
import org.springframework.integration.history.MessageHistory;
import org.springframework.integration.message.GenericMessage;
@@ -107,7 +119,7 @@ public class BeanFactoryTypeConverterTests {
}
});
MessageHistory history = MessageHistory.read(message);
assertSame(history, typeConverter.convertValue(history, TypeDescriptor.valueOf(MessageHeaders.class), TypeDescriptor.valueOf(MessageHeaders.class)));
assertSame(history, typeConverter.convertValue(history, TypeDescriptor.valueOf(MessageHistory.class), TypeDescriptor.valueOf(MessageHistory.class)));
}
@Test
@@ -139,6 +151,128 @@ public class BeanFactoryTypeConverterTests {
assertEquals("foo", typeConverter.convertValue(object, TypeDescriptor.valueOf(Object.class), TypeDescriptor.valueOf(String.class)));
}
@SuppressWarnings("unchecked")
@Test
public void testMapOfMapOfCollectionIsConverted() {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
DefaultConversionService conversionService = new DefaultConversionService();
conversionService.addConverter(new Converter<Foo, Bar>() {
public Bar convert(Foo source) {
return new Bar();
}
});
BeanFactoryTypeConverter typeConverter = new BeanFactoryTypeConverter(conversionService);
beanFactory.setConversionService(conversionService);
typeConverter.setBeanFactory(beanFactory);
Map<String, Map<String, Set<Foo>>> foos;
Map<String, Map<String, Set<Bar>>> bars;
TypeDescriptor sourceType = TypeDescriptor.map(Map.class, null, null);
TypeDescriptor targetType = TypeDescriptor.map(Map.class, TypeDescriptor.valueOf(String.class),
TypeDescriptor.map(Map.class, TypeDescriptor.valueOf(String.class),
TypeDescriptor.collection(Set.class, TypeDescriptor.valueOf(Bar.class))));
Set<Foo> fooSet = new HashSet<Foo>();
fooSet.add(new Foo());
Map<String, Set<Foo>> fooMap = new HashMap<String, Set<Foo>>();
fooMap.put("foo", fooSet);
foos = new HashMap<String, Map<String, Set<Foo>>>();
foos.put("foo", fooMap);
bars = (Map<String, Map<String, Set<Bar>>>) typeConverter.convertValue(foos, sourceType, targetType);
assertTrue(bars.get("foo").get("foo").iterator().next() instanceof Bar);
Service service = new Service();
MethodInvokingMessageProcessor<Service> processor = new MethodInvokingMessageProcessor<Service>(service, "handle");
processor.setConversionService(conversionService);
ServiceActivatingHandler handler = new ServiceActivatingHandler(processor);
QueueChannel replyChannel = new QueueChannel();
handler.setOutputChannel(replyChannel);
handler.handleMessage(new GenericMessage<Map<String, Map<String, Set<Foo>>>>(foos));
Message<?> message = replyChannel.receive(0);
assertNotNull(message);
assertEquals("bar", message.getPayload());
}
@Test
public void testCollectionIsConverted() {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
DefaultConversionService conversionService = new DefaultConversionService();
conversionService.addConverter(new Converter<Foo, Bar>() {
public Bar convert(Foo source) {
return new Bar();
}
});
BeanFactoryTypeConverter typeConverter = new BeanFactoryTypeConverter(conversionService);
beanFactory.setConversionService(conversionService);
typeConverter.setBeanFactory(beanFactory);
Service service = new Service();
MethodInvokingMessageProcessor<Service> processor = new MethodInvokingMessageProcessor<Service>(service, "handle");
processor.setConversionService(conversionService);
ServiceActivatingHandler handler = new ServiceActivatingHandler(processor);
QueueChannel replyChannel = new QueueChannel();
handler.setOutputChannel(replyChannel);
handler.handleMessage(new GenericMessage<Collection<Foo>>(Collections.singletonList(new Foo())));
Message<?> message = replyChannel.receive(0);
assertNotNull(message);
assertEquals("baz", message.getPayload());
}
@Test
public void testNullArg() {
DefaultConversionService conversionService = new DefaultConversionService();
BeanFactoryTypeConverter typeConverter = new BeanFactoryTypeConverter(conversionService);
Object foo = typeConverter.convertValue(null, null, TypeDescriptor.valueOf(Bar.class));
assertNull(foo);
}
@Test
public void testVoidArg() {
DefaultConversionService conversionService = new DefaultConversionService();
BeanFactoryTypeConverter typeConverter = new BeanFactoryTypeConverter(conversionService);
Object foo = typeConverter.convertValue(null, null, TypeDescriptor.valueOf(Void.class));
assertNull(foo);
foo = typeConverter.convertValue(null, null, TypeDescriptor.valueOf(Void.TYPE));
assertNull(foo);
}
@Test
public void testEditorWithTargetString() {
DefaultConversionService conversionService = new DefaultConversionService();
BeanFactoryTypeConverter typeConverter = new BeanFactoryTypeConverter(conversionService);
UUID uuid = UUID.randomUUID();
Object foo = typeConverter.convertValue(uuid, TypeDescriptor.valueOf(UUID.class),
TypeDescriptor.valueOf(String.class));
assertEquals(uuid.toString(), foo);
}
@Test
public void testEditorWithTargetFoo() {
DefaultConversionService conversionService = new DefaultConversionService();
final Foo foo = new Foo();
conversionService.addConverter(new Converter<String, Foo>() {
public Foo convert(String source) {
return foo;
}
});
BeanFactoryTypeConverter typeConverter = new BeanFactoryTypeConverter(conversionService);
UUID uuid = UUID.randomUUID();
Object convertedFoo = typeConverter.convertValue(uuid, TypeDescriptor.valueOf(UUID.class),
TypeDescriptor.valueOf(Foo.class));
assertSame(foo, convertedFoo);
}
@Test
public void testDelegateWithTargetUUID() {
DefaultConversionService conversionService = new DefaultConversionService();
BeanFactoryTypeConverter typeConverter = new BeanFactoryTypeConverter(conversionService);
UUID uuid = UUID.randomUUID();
Object converted = typeConverter.convertValue(uuid.toString(), TypeDescriptor.valueOf(String.class),
TypeDescriptor.valueOf(UUID.class));
assertEquals(uuid, converted);
}
@Test
public void initialConcurrency() throws Exception {
ConversionService conversionService = mock(ConversionService.class); // can convert nothing so we drop down to P.E.s
@@ -176,4 +310,25 @@ public class BeanFactoryTypeConverterTests {
assertEquals(4, count.get());
assertFalse(concurrentlyInGetDefaultEditor.get());
}
public static class Foo {
}
public static class Bar {
}
public static class Service {
public String handle(Map<String, Map<String, Set<Bar>>> payload) {
assertTrue(payload.get("foo").get("foo").iterator().next() instanceof Bar);
return "bar";
}
public String handle(Collection<Bar> payload) {
assertTrue(payload.iterator().next() instanceof Bar);
return "baz";
}
}
}