/*
* Copyright 2002-2016 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.router;
import static org.junit.Assert.fail;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import java.io.Serializable;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.junit.Test;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHandlingException;
import org.springframework.integration.channel.QueueChannel;
import org.springframework.messaging.support.GenericMessage;
/**
* @author Mark Fisher
* @author Oleg Zhurakousky
* @author Gunnar Hillert
*/
public class PayloadTypeRouterTests {
@Test
public void resolveExactMatch() {
QueueChannel stringChannel = new QueueChannel();
QueueChannel integerChannel = new QueueChannel();
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
beanFactory.registerSingleton("stringChannel", stringChannel);
beanFactory.registerSingleton("integerChannel", integerChannel);
Map<String, String> payloadTypeChannelMap = new ConcurrentHashMap<String, String>();
payloadTypeChannelMap.put(String.class.getName(), "stringChannel");
payloadTypeChannelMap.put(Integer.class.getName(), "integerChannel");
PayloadTypeRouter router = new PayloadTypeRouter();
router.setChannelMappings(payloadTypeChannelMap);
router.setBeanFactory(beanFactory);
Message<String> message1 = new GenericMessage<String>("test");
Message<Integer> message2 = new GenericMessage<Integer>(123);
assertEquals(1, router.getChannelKeys(message1).size());
assertNull(stringChannel.receive(0));
router.handleMessage(message1);
assertEquals(message1, stringChannel.receive(0));
assertEquals(1, router.getChannelKeys(message2).size());
assertNull(integerChannel.receive(0));
router.handleMessage(message2);
assertEquals(message2, integerChannel.receive(0));
// validate dynamics
QueueChannel newChannel = new QueueChannel();
beanFactory.registerSingleton("newChannel", newChannel);
router.setChannelMapping(String.class.getName(), "newChannel");
assertEquals(1, router.getChannelKeys(message1).size());
assertNull(newChannel.receive(0));
router.handleMessage(message1);
assertEquals(message1, newChannel.receive(0));
// validate exception is thrown if mappings were removed and
// channelResolutionRequires = true (which is the default)
router.removeChannelMapping(String.class.getName());
router.removeChannelMapping(Integer.class.getName());
router.setResolutionRequired(true);
try {
router.handleMessage(message1);
fail();
}
catch (Exception e) {
// ignore
}
}
@Test
public void resolveSubclass() {
QueueChannel defaultChannel = new QueueChannel();
defaultChannel.setBeanName("defaultChannel");
QueueChannel numberChannel = new QueueChannel();
numberChannel.setBeanName("numberChannel");
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
beanFactory.registerSingleton("defaultChannel", defaultChannel);
beanFactory.registerSingleton("numberChannel", numberChannel);
Map<String, String> payloadTypeChannelMap = new ConcurrentHashMap<String, String>();
payloadTypeChannelMap.put(Number.class.getName(), "numberChannel");
PayloadTypeRouter router = new PayloadTypeRouter();
router.setChannelMappings(payloadTypeChannelMap);
router.setBeanFactory(beanFactory);
router.setDefaultOutputChannel(defaultChannel);
Message<Integer> message = new GenericMessage<Integer>(99);
router.handleMessage(message);
Message<?> result = numberChannel.receive(0);
assertNotNull(result);
assertEquals(99, result.getPayload());
assertNull(defaultChannel.receive(0));
// validate dynamics
QueueChannel newChannel = new QueueChannel();
beanFactory.registerSingleton("newChannel", newChannel);
router.setChannelMapping(Integer.class.getName(), "newChannel");
assertEquals(1, router.getChannelKeys(message).size());
router.handleMessage(message);
result = newChannel.receive(10);
assertNotNull(result);
}
@Test
public void exactMatchFavoredOverSuperclass() {
QueueChannel defaultChannel = new QueueChannel();
defaultChannel.setBeanName("defaultChannel");
QueueChannel numberChannel = new QueueChannel();
numberChannel.setBeanName("numberChannel");
QueueChannel integerChannel = new QueueChannel();
integerChannel.setBeanName("integerChannel");
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
beanFactory.registerSingleton("defaultChannel", defaultChannel);
beanFactory.registerSingleton("numberChannel", numberChannel);
beanFactory.registerSingleton("integerChannel", integerChannel);
Map<String, String> payloadTypeChannelMap = new ConcurrentHashMap<String, String>();
payloadTypeChannelMap.put(Number.class.getName(), "numberChannel");
payloadTypeChannelMap.put(Integer.class.getName(), "integerChannel");
PayloadTypeRouter router = new PayloadTypeRouter();
router.setBeanFactory(beanFactory);
router.setChannelMappings(payloadTypeChannelMap);
router.setDefaultOutputChannel(defaultChannel);
Message<Integer> message = new GenericMessage<Integer>(99);
router.handleMessage(message);
Message<?> result = integerChannel.receive(0);
assertNotNull(result);
assertEquals(99, result.getPayload());
assertNull(numberChannel.receive(0));
assertNull(defaultChannel.receive(0));
}
@Test
public void interfaceMatch() {
QueueChannel defaultChannel = new QueueChannel();
defaultChannel.setBeanName("defaultChannel");
QueueChannel comparableChannel = new QueueChannel();
comparableChannel.setBeanName("comparableChannel");
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
beanFactory.registerSingleton("defaultChannel", defaultChannel);
beanFactory.registerSingleton("comparableChannel", comparableChannel);
Map<String, String> payloadTypeChannelMap = new ConcurrentHashMap<String, String>();
payloadTypeChannelMap.put(Comparable.class.getName(), "comparableChannel");
PayloadTypeRouter router = new PayloadTypeRouter();
router.setBeanFactory(beanFactory);
router.setChannelMappings(payloadTypeChannelMap);
router.setDefaultOutputChannel(defaultChannel);
Message<Integer> message = new GenericMessage<Integer>(99);
router.handleMessage(message);
Message<?> result = comparableChannel.receive(0);
assertNotNull(result);
assertEquals(99, result.getPayload());
assertNull(defaultChannel.receive(0));
}
@Test
public void extendedInterfaceMatch() {
QueueChannel defaultChannel = new QueueChannel();
defaultChannel.setBeanName("defaultChannel");
QueueChannel i2Channel = new QueueChannel();
i2Channel.setBeanName("i2Channel");
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
beanFactory.registerSingleton("defaultChannel", defaultChannel);
beanFactory.registerSingleton("i2Channel", i2Channel);
Map<String, String> payloadTypeChannelMap = new ConcurrentHashMap<String, String>();
payloadTypeChannelMap.put(I2.class.getName(), "i2Channel");
PayloadTypeRouter router = new PayloadTypeRouter();
router.setBeanFactory(beanFactory);
router.setChannelMappings(payloadTypeChannelMap);
router.setDefaultOutputChannel(defaultChannel);
Message<C1> message = new GenericMessage<C1>(new C1());
router.handleMessage(message);
Message<?> result = i2Channel.receive(0);
assertNotNull(result);
}
@Test
public void higherWeightInterface() {
QueueChannel defaultChannel = new QueueChannel();
defaultChannel.setBeanName("defaultChannel");
QueueChannel serializableChannel = new QueueChannel();
serializableChannel.setBeanName("serializableChannel");
QueueChannel i3Channel = new QueueChannel();
i3Channel.setBeanName("i3Channel");
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
beanFactory.registerSingleton("defaultChannel", defaultChannel);
beanFactory.registerSingleton("serializableChannel", serializableChannel);
beanFactory.registerSingleton("i3Channel", i3Channel);
Map<String, String> payloadTypeChannelMap = new ConcurrentHashMap<String, String>();
payloadTypeChannelMap.put(Serializable.class.getName(), "serializableChannel");
payloadTypeChannelMap.put(I3.class.getName(), "i3Channel");
PayloadTypeRouter router = new PayloadTypeRouter();
router.setBeanFactory(beanFactory);
router.setChannelMappings(payloadTypeChannelMap);
router.setDefaultOutputChannel(defaultChannel);
Message<C1> message = new GenericMessage<C1>(new C1());
router.handleMessage(message);
assertNotNull(serializableChannel.receive(0));
assertNull(i3Channel.receive(0));
}
@Test
public void superclassWinsOverDistantInterface() {
QueueChannel defaultChannel = new QueueChannel();
defaultChannel.setBeanName("defaultChannel");
QueueChannel c3Channel = new QueueChannel();
c3Channel.setBeanName("c3Channel");
QueueChannel i4Channel = new QueueChannel();
i4Channel.setBeanName("i4Channel");
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
beanFactory.registerSingleton("defaultChannel", defaultChannel);
beanFactory.registerSingleton("c3Channel", c3Channel);
beanFactory.registerSingleton("i4Channel", i4Channel);
Map<String, String> payloadTypeChannelMap = new ConcurrentHashMap<String, String>();
payloadTypeChannelMap.put(C3.class.getName(), "c3Channel");
payloadTypeChannelMap.put(I4.class.getName(), "i4Channel");
PayloadTypeRouter router = new PayloadTypeRouter();
router.setBeanFactory(beanFactory);
router.setChannelMappings(payloadTypeChannelMap);
router.setDefaultOutputChannel(defaultChannel);
Message<C1> message = new GenericMessage<C1>(new C1());
router.handleMessage(message);
Message<?> result = c3Channel.receive(0);
assertNotNull(result);
}
@Test
public void directInterfaceOverTwoHopSuperclass() {
QueueChannel defaultChannel = new QueueChannel();
defaultChannel.setBeanName("defaultChannel");
QueueChannel c3Channel = new QueueChannel();
c3Channel.setBeanName("c3Channel");
QueueChannel i1AChannel = new QueueChannel();
i1AChannel.setBeanName("i1AChannel");
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
beanFactory.registerSingleton("defaultChannel", defaultChannel);
beanFactory.registerSingleton("c3Channel", c3Channel);
beanFactory.registerSingleton("i1AChannel", i1AChannel);
Map<String, String> payloadTypeChannelMap = new ConcurrentHashMap<String, String>();
payloadTypeChannelMap.put(C3.class.getName(), "c3Channel");
payloadTypeChannelMap.put(I1A.class.getName(), "i1AChannel");
PayloadTypeRouter router = new PayloadTypeRouter();
router.setBeanFactory(beanFactory);
router.setChannelMappings(payloadTypeChannelMap);
router.setDefaultOutputChannel(defaultChannel);
Message<C1> message = new GenericMessage<C1>(new C1());
router.handleMessage(message);
Message<?> result = i1AChannel.receive(0);
assertNotNull(result);
}
@Test
public void directInterfaceFavoredOverSuperclass() {
QueueChannel defaultChannel = new QueueChannel();
defaultChannel.setBeanName("defaultChannel");
QueueChannel numberChannel = new QueueChannel();
numberChannel.setBeanName("numberChannel");
QueueChannel comparableChannel = new QueueChannel();
comparableChannel.setBeanName("comparableChannel");
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
beanFactory.registerSingleton("defaultChannel", defaultChannel);
beanFactory.registerSingleton("numberChannel", numberChannel);
beanFactory.registerSingleton("comparableChannel", comparableChannel);
Map<String, String> payloadTypeChannelMap = new ConcurrentHashMap<String, String>();
payloadTypeChannelMap.put(Number.class.getName(), "numberChannel");
payloadTypeChannelMap.put(Comparable.class.getName(), "comparableChannel");
PayloadTypeRouter router = new PayloadTypeRouter();
router.setBeanFactory(beanFactory);
router.setChannelMappings(payloadTypeChannelMap);
router.setDefaultOutputChannel(defaultChannel);
Message<Integer> message = new GenericMessage<Integer>(99);
router.handleMessage(message);
Message<?> result = comparableChannel.receive(0);
assertNotNull(result);
assertEquals(99, result.getPayload());
assertNull(numberChannel.receive(0));
assertNull(defaultChannel.receive(0));
// validate dynamics
QueueChannel newChannel = new QueueChannel();
beanFactory.registerSingleton("newChannel", newChannel);
router.setChannelMapping(Integer.class.getName(), "newChannel");
assertEquals(1, router.getChannelKeys(message).size());
router.handleMessage(message);
result = newChannel.receive(10);
assertNotNull(result);
}
@Test(expected = MessageHandlingException.class)
public void ambiguityFailure() throws Exception {
QueueChannel defaultChannel = new QueueChannel();
defaultChannel.setBeanName("defaultChannel");
QueueChannel serializableChannel = new QueueChannel();
serializableChannel.setBeanName("serializableChannel");
QueueChannel comparableChannel = new QueueChannel();
comparableChannel.setBeanName("comparableChannel");
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
beanFactory.registerSingleton("defaultChannel", defaultChannel);
beanFactory.registerSingleton("serializableChannel", serializableChannel);
beanFactory.registerSingleton("comparableChannel", comparableChannel);
Map<String, String> payloadTypeChannelMap = new ConcurrentHashMap<String, String>();
payloadTypeChannelMap.put(Serializable.class.getName(), "serializableChannel");
payloadTypeChannelMap.put(Comparable.class.getName(), "comparableChannel");
PayloadTypeRouter router = new PayloadTypeRouter();
router.setBeanFactory(beanFactory);
router.setChannelMappings(payloadTypeChannelMap);
router.setDefaultOutputChannel(defaultChannel);
Message<String> message = new GenericMessage<String>("test");
router.handleMessage(message);
}
@Test
public void superclassFavoredOverIndirectInterface() {
QueueChannel defaultChannel = new QueueChannel();
defaultChannel.setBeanName("defaultChannel");
QueueChannel numberChannel = new QueueChannel();
numberChannel.setBeanName("numberChannel");
QueueChannel serializableChannel = new QueueChannel();
serializableChannel.setBeanName("serializableChannel");
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
beanFactory.registerSingleton("defaultChannel", defaultChannel);
beanFactory.registerSingleton("numberChannel", numberChannel);
beanFactory.registerSingleton("serializableChannel", serializableChannel);
Map<String, String> payloadTypeChannelMap = new ConcurrentHashMap<String, String>();
payloadTypeChannelMap.put(Number.class.getName(), "numberChannel");
payloadTypeChannelMap.put(Serializable.class.getName(), "serializableChannel");
PayloadTypeRouter router = new PayloadTypeRouter();
router.setBeanFactory(beanFactory);
router.setChannelMappings(payloadTypeChannelMap);
router.setDefaultOutputChannel(defaultChannel);
Message<Integer> message = new GenericMessage<Integer>(99);
router.handleMessage(message);
Message<?> result = numberChannel.receive(0);
assertNotNull(result);
assertEquals(99, result.getPayload());
assertNull(serializableChannel.receive(0));
assertNull(defaultChannel.receive(0));
}
@Test
public void resolveByPayloadTypeWithRouterEndpoint() {
QueueChannel stringChannel = new QueueChannel();
QueueChannel integerChannel = new QueueChannel();
stringChannel.setBeanName("stringChannel");
integerChannel.setBeanName("integerChannel");
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
beanFactory.registerSingleton("stringChannel", stringChannel);
beanFactory.registerSingleton("integerChannel", integerChannel);
Map<String, String> payloadTypeChannelMap = new ConcurrentHashMap<String, String>();
payloadTypeChannelMap.put(String.class.getName(), "stringChannel");
payloadTypeChannelMap.put(Integer.class.getName(), "integerChannel");
PayloadTypeRouter router = new PayloadTypeRouter();
router.setBeanFactory(beanFactory);
router.setChannelMappings(payloadTypeChannelMap);
Message<String> message1 = new GenericMessage<String>("test");
Message<Integer> message2 = new GenericMessage<Integer>(123);
router.handleMessage(message1);
router.handleMessage(message2);
Message<?> reply1 = stringChannel.receive(0);
Message<?> reply2 = integerChannel.receive(0);
assertEquals("test", reply1.getPayload());
assertEquals(123, reply2.getPayload());
}
@Test
public void routingToDefaultChannelWhenNoTypeMatches() {
QueueChannel stringChannel = new QueueChannel();
stringChannel.setBeanName("stringChannel");
QueueChannel defaultChannel = new QueueChannel();
defaultChannel.setBeanName("defaultChannel");
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
beanFactory.registerSingleton("stringChannel", stringChannel);
beanFactory.registerSingleton("defaultChannel", defaultChannel);
Map<String, String> payloadTypeChannelMap = new ConcurrentHashMap<String, String>();
payloadTypeChannelMap.put(String.class.getName(), "stringChannel");
PayloadTypeRouter router = new PayloadTypeRouter();
router.setBeanFactory(beanFactory);
router.setChannelMappings(payloadTypeChannelMap);
router.setDefaultOutputChannel(defaultChannel);
Message<String> message1 = new GenericMessage<String>("test");
Message<Integer> message2 = new GenericMessage<Integer>(123);
router.handleMessage(message1);
router.handleMessage(message2);
Message<?> result1 = stringChannel.receive(25);
assertNotNull(result1);
assertEquals("test", result1.getPayload());
Message<?> result2 = defaultChannel.receive(25);
assertNotNull(result2);
assertEquals(123, result2.getPayload());
}
@Test
public void classWinsOverMoreDistantAmbiguousInterfaces() throws Exception {
QueueChannel defaultChannel = new QueueChannel();
defaultChannel.setBeanName("defaultChannel");
QueueChannel i5aChannel = new QueueChannel();
i5aChannel.setBeanName("i5aChannel");
QueueChannel i5bChannel = new QueueChannel();
i5bChannel.setBeanName("i5bChannel");
QueueChannel c2Channel = new QueueChannel();
c2Channel.setBeanName("c2Channel");
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
beanFactory.registerSingleton("defaultChannel", defaultChannel);
beanFactory.registerSingleton("i5aChannel", i5aChannel);
beanFactory.registerSingleton("i5bChannel", i5bChannel);
beanFactory.registerSingleton("c2Channel", c2Channel);
Map<String, String> payloadTypeChannelMap = new ConcurrentHashMap<String, String>();
payloadTypeChannelMap.put(I5A.class.getName(), "i5aChannel");
payloadTypeChannelMap.put(I5B.class.getName(), "i5bChannel");
payloadTypeChannelMap.put(C2.class.getName(), "c2Channel");
PayloadTypeRouter router = new PayloadTypeRouter();
router.setBeanFactory(beanFactory);
router.setChannelMappings(payloadTypeChannelMap);
router.setDefaultOutputChannel(defaultChannel);
Message<C1> message = new GenericMessage<C1>(new C1());
router.handleMessage(message);
assertNotNull(c2Channel.receive(100));
}
@Test(expected = MessageHandlingException.class)
public void classLosesOverLessDistantAmbiguousInterfaces() throws Exception {
QueueChannel defaultChannel = new QueueChannel();
defaultChannel.setBeanName("defaultChannel");
QueueChannel i2Channel = new QueueChannel();
i2Channel.setBeanName("i2Channel");
QueueChannel serializableChannel = new QueueChannel();
serializableChannel.setBeanName("serializableChannel");
QueueChannel c3Channel = new QueueChannel();
c3Channel.setBeanName("c3Channel");
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
beanFactory.registerSingleton("defaultChannel", defaultChannel);
beanFactory.registerSingleton("i2Channel", i2Channel);
beanFactory.registerSingleton("serializableChannel", serializableChannel);
beanFactory.registerSingleton("c3Channel", c3Channel);
Map<String, String> payloadTypeChannelMap = new ConcurrentHashMap<String, String>();
payloadTypeChannelMap.put(I2.class.getName(), "i2Channel");
payloadTypeChannelMap.put(Serializable.class.getName(), "serializableChannel");
payloadTypeChannelMap.put(C3.class.getName(), "c3Channel");
PayloadTypeRouter router = new PayloadTypeRouter();
router.setBeanFactory(beanFactory);
router.setChannelMappings(payloadTypeChannelMap);
router.setDefaultOutputChannel(defaultChannel);
Message<C1> message = new GenericMessage<C1>(new C1());
router.handleMessage(message);
}
@Test(expected = MessageHandlingException.class)
public void classLosesOverAmbiguousInterfacesAtSameLevel() throws Exception {
QueueChannel defaultChannel = new QueueChannel();
defaultChannel.setBeanName("defaultChannel");
QueueChannel i1aChannel = new QueueChannel();
i1aChannel.setBeanName("i1aChannel");
QueueChannel i1bChannel = new QueueChannel();
i1bChannel.setBeanName("i1bChannel");
QueueChannel c2Channel = new QueueChannel();
c2Channel.setBeanName("c2Channel");
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
beanFactory.registerSingleton("defaultChannel", defaultChannel);
beanFactory.registerSingleton("i1aChannel", i1aChannel);
beanFactory.registerSingleton("i1bChannel", i1bChannel);
beanFactory.registerSingleton("c2Channel", c2Channel);
Map<String, String> payloadTypeChannelMap = new ConcurrentHashMap<String, String>();
payloadTypeChannelMap.put(I1A.class.getName(), "i1aChannel");
payloadTypeChannelMap.put(I1B.class.getName(), "i2bChannel");
payloadTypeChannelMap.put(C2.class.getName(), "c2Channel");
PayloadTypeRouter router = new PayloadTypeRouter();
router.setBeanFactory(beanFactory);
router.setChannelMappings(payloadTypeChannelMap);
router.setDefaultOutputChannel(defaultChannel);
Message<C1> message = new GenericMessage<C1>(new C1());
router.handleMessage(message);
}
@SuppressWarnings("serial")
public static class C1 extends C2 implements I1A, I1B { }
public interface I1A extends Serializable { }
public interface I1B extends I2 { }
public interface I2 extends I3 { }
public interface I3 extends I4 { }
public interface I4 extends I5A, I5B { }
public interface I5A { }
public interface I5B { }
public static class C2 extends C3 { }
public static class C3 implements I4 { }
}