/* * Copyright 2002-2017 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.integration.util; import static org.hamcrest.Matchers.instanceOf; import static org.junit.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.assertThat; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; 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; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import org.junit.Test; import org.springframework.beans.SimpleTypeConverter; 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.channel.QueueChannel; import org.springframework.integration.handler.MethodInvokingMessageProcessor; import org.springframework.integration.handler.ServiceActivatingHandler; import org.springframework.integration.history.MessageHistory; import org.springframework.integration.support.context.NamedComponent; import org.springframework.messaging.Message; import org.springframework.messaging.MessageHeaders; import org.springframework.messaging.support.GenericMessage; /** * @author Oleg Zhurakousky * @author Gary Russell * @author Gunnar Hillert * @author Artem Bilan * */ public class BeanFactoryTypeConverterTests { @SuppressWarnings("unchecked") @Test public void testEmptyCollectionConversion() { BeanFactoryTypeConverter typeConverter = new BeanFactoryTypeConverter(); List<String> sourceObject = new ArrayList<String>(); ArrayList<BeanFactoryTypeConverterTests> convertedCollection = (ArrayList<BeanFactoryTypeConverterTests>) typeConverter.convertValue(sourceObject, TypeDescriptor.forObject(sourceObject), TypeDescriptor.forObject(new ArrayList<BeanFactoryTypeConverterTests>())); assertEquals(sourceObject, convertedCollection); } @Test public void testToStringConversion() { BeanFactoryTypeConverter typeConverter = new BeanFactoryTypeConverter(); typeConverter.setBeanFactory(new DefaultListableBeanFactory()); String converted = (String) typeConverter.convertValue(1234, TypeDescriptor.valueOf(Integer.class), TypeDescriptor.valueOf(String.class)); assertEquals("1234", converted); } @Test public void testToNonStringConversionNotSupportedByGenericConversionService() { BeanFactoryTypeConverter typeConverter = new BeanFactoryTypeConverter(); typeConverter.setBeanFactory(new DefaultListableBeanFactory()); @SuppressWarnings("unchecked") Collection<Integer> converted = (Collection<Integer>) typeConverter.convertValue(1234, TypeDescriptor.valueOf(Integer.class), TypeDescriptor.forObject(new ArrayList<Integer>(Arrays.asList(1)))); assertEquals(Arrays.asList(1234), converted); } @Test public void testMessageHeadersNotConverted() { BeanFactoryTypeConverter typeConverter = new BeanFactoryTypeConverter(); typeConverter.setBeanFactory(new DefaultListableBeanFactory()); MessageHeaders headers = new GenericMessage<String>("foo").getHeaders(); assertSame(headers, typeConverter.convertValue(headers, TypeDescriptor.valueOf(MessageHeaders.class), TypeDescriptor.valueOf(MessageHeaders.class))); } @Test public void testMessageHistoryNotConverted() { BeanFactoryTypeConverter typeConverter = new BeanFactoryTypeConverter(); typeConverter.setBeanFactory(new DefaultListableBeanFactory()); Message<String> message = new GenericMessage<String>("foo"); message = MessageHistory.write(message, new NamedComponent() { @Override public String getComponentName() { return "bar"; } @Override public String getComponentType() { return "baz"; } }); MessageHistory history = MessageHistory.read(message); assertSame(history, typeConverter.convertValue(history, TypeDescriptor.valueOf(MessageHistory.class), TypeDescriptor.valueOf(MessageHistory.class))); } @Test public void testByteArrayNotConverted() { BeanFactoryTypeConverter typeConverter = new BeanFactoryTypeConverter(); typeConverter.setBeanFactory(new DefaultListableBeanFactory()); byte[] bytes = new byte[1]; assertSame(bytes, typeConverter.convertValue(bytes, TypeDescriptor.valueOf(byte[].class), TypeDescriptor.valueOf(byte[].class))); } @Test public void testStringToObjectNotConverted() { BeanFactoryTypeConverter typeConverter = new BeanFactoryTypeConverter(); typeConverter.setBeanFactory(new DefaultListableBeanFactory()); String string = "foo"; assertSame(string, typeConverter.convertValue(string, TypeDescriptor.valueOf(String.class), TypeDescriptor.valueOf(Object.class))); } @Test public void testObjectToStringIsConverted() { ConversionService conversionService = mock(ConversionService.class); when(conversionService.canConvert(any(TypeDescriptor.class), any(TypeDescriptor.class))) .thenReturn(true); when(conversionService.convert(any(), any(TypeDescriptor.class), any(TypeDescriptor.class))) .thenReturn("foo"); BeanFactoryTypeConverter typeConverter = new BeanFactoryTypeConverter(conversionService); typeConverter.setBeanFactory(new DefaultListableBeanFactory()); Object object = new Object(); 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>() { @Override 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); assertThat(bars.get("foo").get("foo").iterator().next(), instanceOf(Bar.class)); Service service = new Service(); MethodInvokingMessageProcessor<Service> processor = new MethodInvokingMessageProcessor<>(service, "handle"); processor.setConversionService(conversionService); processor.setUseSpelInvoker(true); 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>() { @Override 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, "handle"); processor.setConversionService(conversionService); processor.setUseSpelInvoker(true); 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(10000); 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>() { @Override 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 final BeanFactoryTypeConverter beanFactoryTypeConverter = new BeanFactoryTypeConverter(conversionService); ConfigurableBeanFactory beanFactory = mock(ConfigurableBeanFactory.class); SimpleTypeConverter typeConverter = spy(new SimpleTypeConverter()); when(beanFactory.getTypeConverter()).thenReturn(typeConverter); final AtomicBoolean inGetDefaultEditor = new AtomicBoolean(); final AtomicBoolean concurrentlyInGetDefaultEditor = new AtomicBoolean(); final AtomicInteger count = new AtomicInteger(); doAnswer(invocation -> { count.incrementAndGet(); Thread.sleep(500); concurrentlyInGetDefaultEditor.set(inGetDefaultEditor.getAndSet(true)); Thread.sleep(500); inGetDefaultEditor.set(false); return invocation.callRealMethod(); }).when(typeConverter).getDefaultEditor(UUID.class); beanFactoryTypeConverter.setBeanFactory(beanFactory); final TypeDescriptor sourceType = TypeDescriptor.valueOf(UUID.class); final TypeDescriptor targetType = TypeDescriptor.valueOf(String.class); ExecutorService exec = Executors.newFixedThreadPool(2); Runnable test = () -> { beanFactoryTypeConverter.canConvert(sourceType, targetType); beanFactoryTypeConverter.convertValue(UUID.randomUUID(), sourceType, targetType); }; exec.execute(test); exec.execute(test); exec.shutdown(); assertTrue(exec.awaitTermination(10, TimeUnit.SECONDS)); 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) { assertThat(payload.get("foo").get("foo").iterator().next(), instanceOf(Bar.class)); return "bar"; } public String handle(Collection<Bar> payload) { assertThat(payload.iterator().next(), instanceOf(Bar.class)); return "baz"; } } }