INT-3816: Superclass Mapping for EMExcTypeRouter
JIRA: https://jira.spring.io/browse/INT-3816 Add `cause` `supperclass` mapping ability to the `ErrorMessageExceptionTypeRouter` Address PR comments: Introduce `classNameMappings` to store exception classes on the mapping population. Override all `@ManagedOperation` s to modify `classNameMappings` as well. Address PR comments: * Remove redundant local variable * Fix concurrency access around `channelMapping` * Add `ClassNotFoundException` test for the `ErrorMessageExceptionTypeRouter` * Add `What's New` note about the `ClassNotFoundException` during init Get rid of `synchronized`, use atomic changes Additional polishing to avoid `ConcurrentModificationException ` Polishing
This commit is contained in:
committed by
Gary Russell
parent
1760572c8b
commit
2d33495437
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
* 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.
|
||||
@@ -19,15 +19,13 @@ package org.springframework.integration.router;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.integration.support.channel.BeanFactoryChannelResolver;
|
||||
import org.springframework.integration.support.management.MappingMessageRouterManagement;
|
||||
import org.springframework.jmx.export.annotation.ManagedAttribute;
|
||||
import org.springframework.jmx.export.annotation.ManagedOperation;
|
||||
@@ -52,7 +50,7 @@ import org.springframework.util.StringUtils;
|
||||
*/
|
||||
public abstract class AbstractMappingMessageRouter extends AbstractMessageRouter implements MappingMessageRouterManagement {
|
||||
|
||||
private volatile Map<String, String> channelMappings = new ConcurrentHashMap<String, String>();
|
||||
protected volatile Map<String, String> channelMappings = new ConcurrentHashMap<String, String>();
|
||||
|
||||
private volatile String prefix;
|
||||
|
||||
@@ -70,9 +68,8 @@ public abstract class AbstractMappingMessageRouter extends AbstractMessageRouter
|
||||
@ManagedAttribute
|
||||
public void setChannelMappings(Map<String, String> channelMappings) {
|
||||
Assert.notNull(channelMappings, "'channelMappings' must not be null");
|
||||
Map<String, String> newChannelMappings = new ConcurrentHashMap<String, String>();
|
||||
newChannelMappings.putAll(channelMappings);
|
||||
this.doSetChannelMappings(newChannelMappings);
|
||||
Map<String, String> newChannelMappings = new ConcurrentHashMap<String, String>(channelMappings);
|
||||
doSetChannelMappings(newChannelMappings);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -108,7 +105,7 @@ public abstract class AbstractMappingMessageRouter extends AbstractMessageRouter
|
||||
@Override
|
||||
@ManagedAttribute
|
||||
public Map<String, String> getChannelMappings() {
|
||||
return Collections.unmodifiableMap(this.channelMappings);
|
||||
return new HashMap<String, String>(this.channelMappings);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -119,7 +116,9 @@ public abstract class AbstractMappingMessageRouter extends AbstractMessageRouter
|
||||
@Override
|
||||
@ManagedOperation
|
||||
public void setChannelMapping(String key, String channelName) {
|
||||
this.channelMappings.put(key, channelName);
|
||||
Map<String, String> newChannelMappings = new ConcurrentHashMap<String, String>(this.channelMappings);
|
||||
newChannelMappings.put(key, channelName);
|
||||
this.channelMappings = newChannelMappings;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -129,7 +128,9 @@ public abstract class AbstractMappingMessageRouter extends AbstractMessageRouter
|
||||
@Override
|
||||
@ManagedOperation
|
||||
public void removeChannelMapping(String key) {
|
||||
this.channelMappings.remove(key);
|
||||
Map<String, String> newChannelMappings = new ConcurrentHashMap<String, String>(this.channelMappings);
|
||||
newChannelMappings.remove(key);
|
||||
this.channelMappings = newChannelMappings;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -176,8 +177,7 @@ public abstract class AbstractMappingMessageRouter extends AbstractMessageRouter
|
||||
Map<String, String> oldChannelMappings = this.channelMappings;
|
||||
this.channelMappings = newChannelMappings;
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Channel mappings:" + oldChannelMappings
|
||||
+ " replaced with:" + newChannelMappings);
|
||||
logger.debug("Channel mappings: " + oldChannelMappings + " replaced with: " + newChannelMappings);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* 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.
|
||||
@@ -18,20 +18,93 @@ package org.springframework.integration.router;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.springframework.jmx.export.annotation.ManagedAttribute;
|
||||
import org.springframework.jmx.export.annotation.ManagedOperation;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* A Message Router that resolves the target {@link MessageChannel} for
|
||||
* messages whose payload is an Exception. The channel resolution is based upon
|
||||
* the most specific cause of the error for which a channel-mapping exists.
|
||||
*
|
||||
* <p>
|
||||
* The channel-mapping can be specified for the super classes to avoid mapping duplication
|
||||
* for the particular exception implementation.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @author Oleg Zhurakousky
|
||||
*/
|
||||
* @author Artem Bilan
|
||||
*/
|
||||
public class ErrorMessageExceptionTypeRouter extends AbstractMappingMessageRouter {
|
||||
|
||||
private volatile Map<String, Class<?>> classNameMappings = new ConcurrentHashMap<String, Class<?>>();
|
||||
|
||||
private volatile boolean initialized;
|
||||
|
||||
@Override
|
||||
@ManagedAttribute
|
||||
public void setChannelMappings(Map<String, String> channelMappings) {
|
||||
super.setChannelMappings(channelMappings);
|
||||
if (this.initialized) {
|
||||
populateClassNameMapping(channelMappings.keySet());
|
||||
}
|
||||
}
|
||||
|
||||
private void populateClassNameMapping(Set<String> classNames) {
|
||||
Map<String, Class<?>> newClassNameMappings = new ConcurrentHashMap<String, Class<?>>();
|
||||
for (String className : classNames) {
|
||||
newClassNameMappings.put(className, resolveClassFromName(className));
|
||||
}
|
||||
this.classNameMappings = newClassNameMappings;
|
||||
}
|
||||
|
||||
private Class<?> resolveClassFromName(String className) {
|
||||
try {
|
||||
return ClassUtils.forName(className, getApplicationContext().getClassLoader());
|
||||
}
|
||||
catch (ClassNotFoundException e) {
|
||||
throw new IllegalStateException("Cannot load class for channel mapping.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ManagedOperation
|
||||
public void setChannelMapping(String key, String channelName) {
|
||||
super.setChannelMapping(key, channelName);
|
||||
Map<String, Class<?>> newClassNameMappings = new ConcurrentHashMap<String, Class<?>>(this.classNameMappings);
|
||||
newClassNameMappings.put(key, resolveClassFromName(key));
|
||||
this.classNameMappings = newClassNameMappings;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ManagedOperation
|
||||
public void removeChannelMapping(String key) {
|
||||
super.removeChannelMapping(key);
|
||||
Map<String, Class<?>> newClassNameMappings = new ConcurrentHashMap<String, Class<?>>(this.classNameMappings);
|
||||
newClassNameMappings.remove(key);
|
||||
this.classNameMappings = newClassNameMappings;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ManagedOperation
|
||||
public void replaceChannelMappings(Properties channelMappings) {
|
||||
super.replaceChannelMappings(channelMappings);
|
||||
populateClassNameMapping(this.channelMappings.keySet());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onInit() throws Exception {
|
||||
super.onInit();
|
||||
populateClassNameMapping(this.channelMappings.keySet());
|
||||
this.initialized = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<Object> getChannelKeys(Message<?> message) {
|
||||
String mostSpecificCause = null;
|
||||
@@ -39,14 +112,17 @@ public class ErrorMessageExceptionTypeRouter extends AbstractMappingMessageRoute
|
||||
if (payload instanceof Throwable) {
|
||||
Throwable cause = (Throwable) payload;
|
||||
while (cause != null) {
|
||||
String causeName = cause.getClass().getName();
|
||||
if (this.getChannelMappings().keySet().contains(causeName)) {
|
||||
mostSpecificCause = causeName;
|
||||
for (Map.Entry<String, Class<?>> entry : this.classNameMappings.entrySet()) {
|
||||
String channelKey = entry.getKey();
|
||||
Class<?> exceptionClass = entry.getValue();
|
||||
if (exceptionClass.isInstance(cause)) {
|
||||
mostSpecificCause = channelKey;
|
||||
}
|
||||
}
|
||||
cause = cause.getCause();
|
||||
}
|
||||
}
|
||||
return Collections.singletonList((Object) mostSpecificCause);
|
||||
return Collections.<Object>singletonList(mostSpecificCause);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
* 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.
|
||||
@@ -47,7 +47,7 @@ public class PayloadTypeRouter extends AbstractMappingMessageRouter {
|
||||
*/
|
||||
@Override
|
||||
protected List<Object> getChannelKeys(Message<?> message) {
|
||||
if (CollectionUtils.isEmpty(this.getChannelMappings())) {
|
||||
if (CollectionUtils.isEmpty(this.channelMappings)) {
|
||||
return null;
|
||||
}
|
||||
Class<?> type = message.getPayload().getClass();
|
||||
@@ -63,7 +63,7 @@ public class PayloadTypeRouter extends AbstractMappingMessageRouter {
|
||||
private String findClosestMatch(Class<?> type, boolean isArray) {
|
||||
int minTypeDiffWeight = Integer.MAX_VALUE;
|
||||
List<String> matches = new ArrayList<String>();
|
||||
for (String candidate : this.getChannelMappings().keySet()) {
|
||||
for (String candidate : this.channelMappings.keySet()) {
|
||||
if (isArray) {
|
||||
if (!candidate.endsWith(ARRAY_SUFFIX)) {
|
||||
continue;
|
||||
@@ -88,7 +88,8 @@ public class PayloadTypeRouter extends AbstractMappingMessageRouter {
|
||||
}
|
||||
if (matches.size() > 1) { // ambiguity
|
||||
throw new IllegalStateException(
|
||||
"Unresolvable ambiguity while attempting to find closest match for [" + type.getName() + "]. Found: " + matches);
|
||||
"Unresolvable ambiguity while attempting to find closest match for [" + type.getName() + "]. " +
|
||||
"Found: " + matches);
|
||||
}
|
||||
if (CollectionUtils.isEmpty(matches)) { // no match
|
||||
return null;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* 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.
|
||||
@@ -23,19 +23,18 @@ import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.messaging.MessageHandlingException;
|
||||
import org.springframework.integration.MessageRejectedException;
|
||||
import org.springframework.integration.channel.QueueChannel;
|
||||
import org.springframework.messaging.support.GenericMessage;
|
||||
import org.springframework.integration.test.util.TestUtils;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageDeliveryException;
|
||||
import org.springframework.messaging.MessageHandlingException;
|
||||
import org.springframework.messaging.support.ErrorMessage;
|
||||
import org.springframework.messaging.support.GenericMessage;
|
||||
|
||||
/**
|
||||
* @author Mark Fisher
|
||||
@@ -74,16 +73,15 @@ public class ErrorMessageExceptionTypeRouterTests {
|
||||
MessageHandlingException error = new MessageHandlingException(failedMessage, "failed", middleCause);
|
||||
ErrorMessage message = new ErrorMessage(error);
|
||||
ErrorMessageExceptionTypeRouter router = new ErrorMessageExceptionTypeRouter();
|
||||
Map<String, String> exceptionTypeChannelMap = new HashMap<String, String>();
|
||||
exceptionTypeChannelMap.put(IllegalArgumentException.class.getName(), "illegalArgumentChannel");
|
||||
exceptionTypeChannelMap.put(RuntimeException.class.getName(), "runtimeExceptionChannel");
|
||||
exceptionTypeChannelMap.put(MessageHandlingException.class.getName(), "messageHandlingExceptionChannel");
|
||||
router.setChannelMappings(exceptionTypeChannelMap);
|
||||
|
||||
router.setBeanFactory(beanFactory);
|
||||
|
||||
router.setApplicationContext(TestUtils.createTestApplicationContext());
|
||||
router.setChannelMapping(IllegalArgumentException.class.getName(), "illegalArgumentChannel");
|
||||
router.setChannelMapping(RuntimeException.class.getName(), "runtimeExceptionChannel");
|
||||
router.setChannelMapping(MessageHandlingException.class.getName(), "messageHandlingExceptionChannel");
|
||||
router.setDefaultOutputChannel(defaultChannel);
|
||||
|
||||
router.handleMessage(message);
|
||||
|
||||
assertNotNull(illegalArgumentChannel.receive(1000));
|
||||
assertNull(defaultChannel.receive(0));
|
||||
assertNull(runtimeExceptionChannel.receive(0));
|
||||
@@ -98,14 +96,14 @@ public class ErrorMessageExceptionTypeRouterTests {
|
||||
MessageHandlingException error = new MessageHandlingException(failedMessage, "failed", middleCause);
|
||||
ErrorMessage message = new ErrorMessage(error);
|
||||
ErrorMessageExceptionTypeRouter router = new ErrorMessageExceptionTypeRouter();
|
||||
Map<String, String> exceptionTypeChannelMap = new HashMap<String, String>();
|
||||
exceptionTypeChannelMap.put(RuntimeException.class.getName(), "runtimeExceptionChannel");
|
||||
exceptionTypeChannelMap.put(MessageHandlingException.class.getName(), "runtimeExceptionChannel");
|
||||
router.setChannelMappings(exceptionTypeChannelMap);
|
||||
router.setBeanFactory(beanFactory);
|
||||
|
||||
router.setApplicationContext(TestUtils.createTestApplicationContext());
|
||||
router.setChannelMapping(RuntimeException.class.getName(), "runtimeExceptionChannel");
|
||||
router.setChannelMapping(MessageHandlingException.class.getName(), "runtimeExceptionChannel");
|
||||
router.setDefaultOutputChannel(defaultChannel);
|
||||
|
||||
router.handleMessage(message);
|
||||
|
||||
assertNotNull(runtimeExceptionChannel.receive(1000));
|
||||
assertNull(illegalArgumentChannel.receive(0));
|
||||
assertNull(defaultChannel.receive(0));
|
||||
@@ -120,12 +118,13 @@ public class ErrorMessageExceptionTypeRouterTests {
|
||||
MessageHandlingException error = new MessageHandlingException(failedMessage, "failed", middleCause);
|
||||
ErrorMessage message = new ErrorMessage(error);
|
||||
ErrorMessageExceptionTypeRouter router = new ErrorMessageExceptionTypeRouter();
|
||||
Map<String, String> exceptionTypeChannelMap = new HashMap<String, String>();
|
||||
exceptionTypeChannelMap.put(MessageHandlingException.class.getName(), "messageHandlingExceptionChannel");
|
||||
router.setChannelMappings(exceptionTypeChannelMap);
|
||||
router.setBeanFactory(beanFactory);
|
||||
router.setApplicationContext(TestUtils.createTestApplicationContext());
|
||||
router.setChannelMapping(MessageHandlingException.class.getName(), "messageHandlingExceptionChannel");
|
||||
router.setDefaultOutputChannel(defaultChannel);
|
||||
|
||||
router.handleMessage(message);
|
||||
|
||||
assertNotNull(messageHandlingExceptionChannel.receive(1000));
|
||||
assertNull(runtimeExceptionChannel.receive(0));
|
||||
assertNull(illegalArgumentChannel.receive(0));
|
||||
@@ -140,8 +139,11 @@ public class ErrorMessageExceptionTypeRouterTests {
|
||||
MessageHandlingException error = new MessageHandlingException(failedMessage, "failed", middleCause);
|
||||
ErrorMessage message = new ErrorMessage(error);
|
||||
ErrorMessageExceptionTypeRouter router = new ErrorMessageExceptionTypeRouter();
|
||||
router.setApplicationContext(TestUtils.createTestApplicationContext());
|
||||
router.setDefaultOutputChannel(defaultChannel);
|
||||
|
||||
router.handleMessage(message);
|
||||
|
||||
assertNotNull(defaultChannel.receive(1000));
|
||||
assertNull(runtimeExceptionChannel.receive(0));
|
||||
assertNull(illegalArgumentChannel.receive(0));
|
||||
@@ -156,12 +158,12 @@ public class ErrorMessageExceptionTypeRouterTests {
|
||||
MessageHandlingException error = new MessageHandlingException(failedMessage, "failed", middleCause);
|
||||
ErrorMessage message = new ErrorMessage(error);
|
||||
ErrorMessageExceptionTypeRouter router = new ErrorMessageExceptionTypeRouter();
|
||||
Map<String, String> exceptionTypeChannelMap = new HashMap<String, String>();
|
||||
exceptionTypeChannelMap.put(MessageDeliveryException.class.getName(), "messageDeliveryExceptionChannel");
|
||||
router.setChannelMappings(exceptionTypeChannelMap);
|
||||
router.setBeanFactory(beanFactory);
|
||||
router.setApplicationContext(TestUtils.createTestApplicationContext());
|
||||
router.setChannelMapping(MessageDeliveryException.class.getName(), "messageDeliveryExceptionChannel");
|
||||
router.setResolutionRequired(true);
|
||||
router.setBeanName("fooRouter");
|
||||
|
||||
try {
|
||||
router.handleMessage(message);
|
||||
fail("MessageDeliveryException expected");
|
||||
@@ -180,14 +182,15 @@ public class ErrorMessageExceptionTypeRouterTests {
|
||||
MessageHandlingException error = new MessageHandlingException(failedMessage, "failed", middleCause);
|
||||
Message<?> message = new GenericMessage<Exception>(error);
|
||||
ErrorMessageExceptionTypeRouter router = new ErrorMessageExceptionTypeRouter();
|
||||
Map<String, String> exceptionTypeChannelMap = new HashMap<String, String>();
|
||||
exceptionTypeChannelMap.put(IllegalArgumentException.class.getName(), "illegalArgumentChannel");
|
||||
exceptionTypeChannelMap.put(RuntimeException.class.getName(), "runtimeExceptionChannel");
|
||||
exceptionTypeChannelMap.put(MessageHandlingException.class.getName(), "messageHandlingExceptionChannel");
|
||||
router.setChannelMappings(exceptionTypeChannelMap);
|
||||
router.setBeanFactory(beanFactory);
|
||||
router.setApplicationContext(TestUtils.createTestApplicationContext());
|
||||
router.setChannelMapping(IllegalArgumentException.class.getName(), "illegalArgumentChannel");
|
||||
router.setChannelMapping(RuntimeException.class.getName(), "runtimeExceptionChannel");
|
||||
router.setChannelMapping(MessageHandlingException.class.getName(), "messageHandlingExceptionChannel");
|
||||
router.setDefaultOutputChannel(defaultChannel);
|
||||
|
||||
router.handleMessage(message);
|
||||
|
||||
assertNotNull(illegalArgumentChannel.receive(1000));
|
||||
assertNull(defaultChannel.receive(0));
|
||||
assertNull(runtimeExceptionChannel.receive(0));
|
||||
@@ -202,17 +205,50 @@ public class ErrorMessageExceptionTypeRouterTests {
|
||||
MessageHandlingException error = new MessageHandlingException(failedMessage, "failed", middleCause);
|
||||
ErrorMessage message = new ErrorMessage(error);
|
||||
ErrorMessageExceptionTypeRouter router = new ErrorMessageExceptionTypeRouter();
|
||||
Map<String, String> exceptionTypeChannelMap = new HashMap<String, String>();
|
||||
exceptionTypeChannelMap.put(IllegalArgumentException.class.getName(), "illegalArgumentChannel");
|
||||
exceptionTypeChannelMap.put(MessageHandlingException.class.getName(), "messageHandlingExceptionChannel");
|
||||
router.setChannelMappings(exceptionTypeChannelMap);
|
||||
router.setBeanFactory(beanFactory);
|
||||
router.setApplicationContext(TestUtils.createTestApplicationContext());
|
||||
router.setChannelMapping(IllegalArgumentException.class.getName(), "illegalArgumentChannel");
|
||||
router.setChannelMapping(MessageHandlingException.class.getName(), "messageHandlingExceptionChannel");
|
||||
router.setDefaultOutputChannel(defaultChannel);
|
||||
|
||||
router.handleMessage(message);
|
||||
|
||||
assertNotNull(illegalArgumentChannel.receive(1000));
|
||||
assertNull(defaultChannel.receive(0));
|
||||
assertNull(runtimeExceptionChannel.receive(0));
|
||||
assertNull(messageHandlingExceptionChannel.receive(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHierarchicalMapping() {
|
||||
IllegalArgumentException rootCause = new IllegalArgumentException("bad argument");
|
||||
MessageHandlingException error = new MessageRejectedException(new GenericMessage<Object>("foo"), "failed", rootCause);
|
||||
ErrorMessage message = new ErrorMessage(error);
|
||||
ErrorMessageExceptionTypeRouter router = new ErrorMessageExceptionTypeRouter();
|
||||
router.setBeanFactory(beanFactory);
|
||||
router.setApplicationContext(TestUtils.createTestApplicationContext());
|
||||
router.setChannelMapping(MessageHandlingException.class.getName(), "messageHandlingExceptionChannel");
|
||||
router.setDefaultOutputChannel(defaultChannel);
|
||||
|
||||
router.handleMessage(message);
|
||||
|
||||
assertNotNull(messageHandlingExceptionChannel.receive(1000));
|
||||
assertNull(defaultChannel.receive(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidMapping() {
|
||||
ErrorMessageExceptionTypeRouter router = new ErrorMessageExceptionTypeRouter();
|
||||
router.setBeanFactory(beanFactory);
|
||||
router.setApplicationContext(TestUtils.createTestApplicationContext());
|
||||
try {
|
||||
router.setChannelMapping("foo", "fooChannel");
|
||||
fail("IllegalStateException expected");
|
||||
}
|
||||
catch (Exception e) {
|
||||
assertThat(e, instanceOf(IllegalStateException.class));
|
||||
assertThat(e.getCause(), instanceOf(ClassNotFoundException.class));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -959,7 +959,13 @@ As such, please read chapter _<<xml-xpath-routing>>_
|
||||
Spring Integration also provides a special type-based router called `ErrorMessageExceptionTypeRouter` for routing Error Messages (Messages whose `payload` is a `Throwable` instance).
|
||||
`ErrorMessageExceptionTypeRouter` is very similar to the `PayloadTypeRouter`.
|
||||
In fact they are almost identical.
|
||||
The only difference is that while `PayloadTypeRouter` navigates the instance hierarchy of a payload instance (e.g., `payload.getClass().getSuperclass()`) to find the most specific type/channel mappings, the `ErrorMessageExceptionTypeRouter` navigates the hierarchy of 'exception causes' (e.g., `payload.getCause()`) to find the most specific `Throwable` type/channel mappings.
|
||||
The only difference is that while `PayloadTypeRouter` navigates the instance hierarchy of a payload instance (e.g., `payload.getClass().getSuperclass()`) to find the most specific type/channel mappings,
|
||||
the `ErrorMessageExceptionTypeRouter` navigates the hierarchy of 'exception causes' (e.g., `payload.getCause()`)
|
||||
to find the most specific `Throwable` type/channel mappings and uses `mappingClass.isInstance(cause)` to match the
|
||||
`cause` to the class or any super class.
|
||||
|
||||
NOTE: Since _version 4.3_ the `ErrorMessageExceptionTypeRouter` loads all mapping classes during the initialization
|
||||
phase to fail-fast for a `ClassNotFoundException`.
|
||||
|
||||
Below is a sample configuration for `ErrorMessageExceptionTypeRouter`.
|
||||
|
||||
|
||||
@@ -97,3 +97,12 @@ See <<http-inbound>> for more information.
|
||||
|
||||
A new factory bean is provided to simplify the configuration of Jsch proxies for SFTP.
|
||||
See <<sftp-proxy-factory-bean>> for more information.
|
||||
|
||||
==== Routers Changes
|
||||
|
||||
The `ErrorMessageExceptionTypeRouter` supports now the `Exception` superclass mappings to avoid duplication
|
||||
for the same channel in case of several inheritors.
|
||||
For this purpose the `ErrorMessageExceptionTypeRouter` loads mapping classes during initialization to fail-fast
|
||||
for a `ClassNotFoundException`.
|
||||
|
||||
See <<router>> for more information.
|
||||
|
||||
Reference in New Issue
Block a user