INT-2431 Improve 'Dispatcher Has No Subscribers'

Add channel name to MessageDeliveryException

'Dispatcher has no subscribers'.

In a large integration flow, it can be difficult to track
down which subscribable channel has no subscribers.

This commit adds to the message text in the form

    for channel someChannelName

to the exception message. For example:

    "Dispatcher has no subscribers for channel myChannel."

Also

    for amqp-channel someAmqpChannelName
    for jms-channel someJMSChannelName
    for redis-channel someJMSChannelName

INT-2431 Polishing

Make component type and name immutable once set to avoid
channels further down the stack frame claiming ownership.

INT-2431 Polishing

* PR Comments - use a new Exception type
* Add support for Redis
* Add WARN logs for AMQP/JMS pub-sub channels

INT-2431 Polishing

PR Comments: tighten up isPubSub field in AMQP-backed channel.

INT-2431 Polishing

Ensure channel name is not 'null' or empty string.
This commit is contained in:
Gary Russell
2012-02-06 19:59:41 -05:00
committed by Oleg Zhurakousky
parent 0b649abfaa
commit f75dc53ab0
14 changed files with 553 additions and 32 deletions

View File

@@ -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.
@@ -30,6 +30,8 @@ import org.springframework.data.redis.listener.adapter.MessageListenerAdapter;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.integration.Message;
import org.springframework.integration.MessageDeliveryException;
import org.springframework.integration.MessageDispatchingException;
import org.springframework.integration.channel.AbstractMessageChannel;
import org.springframework.integration.channel.MessagePublishingErrorHandler;
import org.springframework.integration.core.MessageHandler;
@@ -42,9 +44,11 @@ import org.springframework.integration.support.converter.SimpleMessageConverter;
import org.springframework.integration.util.ErrorHandlingTaskExecutor;
import org.springframework.util.Assert;
import org.springframework.util.ErrorHandler;
import org.springframework.util.StringUtils;
/**
* @author Oleg Zhurakousky
* @author Gary Russell
* @since 2.0
*/
@SuppressWarnings("rawtypes")
@@ -55,7 +59,7 @@ public class SubscribableRedisChannel extends AbstractMessageChannel implements
private final RedisTemplate redisTemplate;
private final String topicName;
private final MessageDispatcher dispatcher = new BroadcastingDispatcher();
private final MessageDispatcher dispatcher = new BroadcastingDispatcher(true);
private volatile boolean initialized;
@@ -171,7 +175,16 @@ public class SubscribableRedisChannel extends AbstractMessageChannel implements
@SuppressWarnings("unused")
public void handleMessage(String s) {
Message<?> siMessage = messageConverter.toMessage(s);
dispatcher.dispatch(siMessage);
try {
dispatcher.dispatch(siMessage);
}
catch (MessageDispatchingException e) {
String topicName = SubscribableRedisChannel.this.topicName;
topicName = StringUtils.hasText(topicName) ? topicName : "unknown";
throw new MessageDeliveryException(siMessage, e.getMessage()
+ " for redis-channel "
+ topicName + ".", e);
}
}
}
}

View File

@@ -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.
@@ -15,21 +15,33 @@
*/
package org.springframework.integration.redis.channel;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import java.lang.reflect.InvocationTargetException;
import java.util.Map;
import java.util.Set;
import org.junit.Test;
import org.mockito.Mockito;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.data.redis.listener.adapter.MessageListenerAdapter;
import org.springframework.integration.Message;
import org.springframework.integration.core.MessageHandler;
import org.springframework.integration.message.GenericMessage;
import org.springframework.integration.redis.rules.RedisAvailable;
import org.springframework.integration.redis.rules.RedisAvailableTests;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import org.springframework.integration.test.util.TestUtils;
import org.springframework.util.ReflectionUtils;
/**
* @author Oleg Zhurakousky
* @author Gary Russell
* @since 2.0
*/
public class SubscribableRedisChannelTests extends RedisAvailableTests{
@@ -56,4 +68,34 @@ public class SubscribableRedisChannelTests extends RedisAvailableTests{
verify(handler, times(3)).handleMessage(Mockito.any(Message.class));
channel.stop();
}
@Test
@RedisAvailable
public void dispatcherHasNoSubscribersTest() throws Exception{
JedisConnectionFactory connectionFactory = new JedisConnectionFactory();
connectionFactory.setPort(7379);
connectionFactory.afterPropertiesSet();
SubscribableRedisChannel channel = new SubscribableRedisChannel(connectionFactory, "si.test.channel.no.subs");
channel.setBeanFactory(mock(BeanFactory.class));
channel.afterPropertiesSet();
RedisMessageListenerContainer container = TestUtils.getPropertyValue(
channel, "container", RedisMessageListenerContainer.class);
@SuppressWarnings("unchecked")
Map<?, Set<MessageListenerAdapter>> channelMapping = (Map<?, Set<MessageListenerAdapter>>) TestUtils
.getPropertyValue(container, "channelMapping");
MessageListenerAdapter listener = channelMapping.entrySet().iterator().next().getValue().iterator().next();
Object delegate = TestUtils.getPropertyValue(listener, "delegate");
try {
ReflectionUtils.findMethod(delegate.getClass(), "handleMessage", String.class).invoke(delegate, "Hello, world!");
fail("Exception expected");
}
catch (InvocationTargetException e) {
Throwable cause = e.getCause();
assertNotNull(cause);
assertEquals("Dispatcher has no subscribers for redis-channel si.test.channel.no.subs.", cause.getMessage());
}
}
}