INT-2285 Implement max-subscribers on Channels

Add a subscriber limit on both unicast and publish-subscribe
channels. This permits detection of inadvertent channel wiring
during context initialization.

Also add a mechanism to globally set these defaults.

Two properties are added to ChannelInitializer. If the
'channelInitializer' bean is declared before a channel, and
these properties are set, it will globally override the
default (Integer.MAX_VALUE) for these properties.

For example:

    <bean id="channelInitializer" class="org.springframework.integration.config.xml.ChannelInitializer">
        <property name="autoCreate" value="true" />
        <property name="defaultMaxUnicastSubscribers" value="1" />
        <property name="defaultMaxMulticastSubscribers" value="2" />
    </bean>

will make the default max-subscribers 1 and 2 for <channel/> and
<publish-subscribe/> channels respectively.

Also applies to module channels (jms, amqp, redis).
This commit is contained in:
Gary Russell
2012-07-10 18:06:16 -04:00
committed by Gunnar Hillert
parent a90a7e8e0b
commit 2ecb14de2a
35 changed files with 606 additions and 78 deletions

View File

@@ -32,15 +32,15 @@ import org.springframework.util.StringUtils;
/**
* Base implementation of {@link MessageChannel} that invokes the subscribed
* {@link MessageHandler handler(s)} by delegating to a {@link MessageDispatcher}.
*
*
* @author Mark Fisher
* @author Oleg Zhurakousky
* @author Gary Russell
*/
public abstract class AbstractSubscribableChannel extends AbstractMessageChannel implements SubscribableChannel {
private final AtomicInteger handlerCounter = new AtomicInteger();
public boolean subscribe(MessageHandler handler) {
MessageDispatcher dispatcher = this.getRequiredDispatcher();
boolean added = dispatcher.addHandler(handler);

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2009 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.
@@ -23,11 +23,12 @@ import org.springframework.integration.dispatcher.UnicastingDispatcher;
/**
* A channel that invokes a single subscriber for each sent Message.
* The invocation will occur in the sender's thread.
*
*
* @author Dave Syer
* @author Mark Fisher
* @author Iwein Fuld
* @author Oleg Zhurakousky
* @author Gary Russell
*/
public class DirectChannel extends AbstractSubscribableChannel {
@@ -57,6 +58,15 @@ public class DirectChannel extends AbstractSubscribableChannel {
this.dispatcher.setFailover(failover);
}
/**
* Specify the maximum number of subscribers supported by the
* channel's dispatcher.
* @param maxSubscribers
*/
public void setMaxSubscribers(int maxSubscribers) {
this.dispatcher.setMaxSubscribers(maxSubscribers);
}
@Override
protected UnicastingDispatcher getDispatcher() {
return this.dispatcher;

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2010 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.
@@ -37,9 +37,10 @@ import org.springframework.util.ErrorHandler;
* {@link Executor} typically does not block the sender's Thread since it
* uses another Thread for the dispatch.</emphasis> (SyncTaskExecutor is an
* exception but would provide no value for this channel. If synchronous
* dispatching is required, a DirectChannel should be used instead).
*
* dispatching is required, a DirectChannel should be used instead).
*
* @author Mark Fisher
* @author Gary Russell
* @since 1.0.3
*/
public class ExecutorChannel extends AbstractSubscribableChannel {
@@ -50,6 +51,8 @@ public class ExecutorChannel extends AbstractSubscribableChannel {
private volatile boolean failover = true;
private volatile int maxSubscribers = Integer.MAX_VALUE;
private volatile LoadBalancingStrategy loadBalancingStrategy;
@@ -89,6 +92,16 @@ public class ExecutorChannel extends AbstractSubscribableChannel {
this.dispatcher.setFailover(failover);
}
/**
* Specify the maximum number of subscribers supported by the
* channel's dispatcher.
* @param maxSubscribers
*/
public void setMaxSubscribers(int maxSubscribers) {
this.maxSubscribers = maxSubscribers;
this.dispatcher.setMaxSubscribers(maxSubscribers);
}
@Override
protected UnicastingDispatcher getDispatcher() {
return this.dispatcher;
@@ -103,6 +116,7 @@ public class ExecutorChannel extends AbstractSubscribableChannel {
}
this.dispatcher = new UnicastingDispatcher(this.executor);
this.dispatcher.setFailover(this.failover);
this.dispatcher.setMaxSubscribers(maxSubscribers);
if (this.loadBalancingStrategy != null) {
this.dispatcher.setLoadBalancingStrategy(this.loadBalancingStrategy);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2010 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.
@@ -24,10 +24,11 @@ import org.springframework.integration.util.ErrorHandlingTaskExecutor;
import org.springframework.util.ErrorHandler;
/**
* A channel that sends Messages to each of its subscribers.
*
* A channel that sends Messages to each of its subscribers.
*
* @author Mark Fisher
* @author Oleg Zhurakousky
* @author Gary Russell
*/
public class PublishSubscribeChannel extends AbstractSubscribableChannel {
@@ -41,6 +42,9 @@ public class PublishSubscribeChannel extends AbstractSubscribableChannel {
private volatile boolean applySequence;
private volatile int maxSubscribers = Integer.MAX_VALUE;
@Override
public String getComponentType(){
return "publish-subscribe-channel";
}
@@ -56,7 +60,7 @@ public class PublishSubscribeChannel extends AbstractSubscribableChannel {
/**
* Create a PublishSubscribeChannel that will invoke the handlers in the
* message sender's thread.
* message sender's thread.
*/
public PublishSubscribeChannel() {
this(null);
@@ -103,6 +107,15 @@ public class PublishSubscribeChannel extends AbstractSubscribableChannel {
this.getDispatcher().setApplySequence(applySequence);
}
/**
* Specify the maximum number of subscribers supported by the
* channel's dispatcher.
* @param maxSubscribers
*/
public void setMaxSubscribers(int maxSubscribers) {
this.maxSubscribers = maxSubscribers;
this.getDispatcher().setMaxSubscribers(maxSubscribers);
}
/**
* Callback method for initialization.
*/
@@ -119,6 +132,7 @@ public class PublishSubscribeChannel extends AbstractSubscribableChannel {
this.dispatcher = new BroadcastingDispatcher(this.executor);
this.dispatcher.setIgnoreFailures(this.ignoreFailures);
this.dispatcher.setApplySequence(this.applySequence);
this.dispatcher.setMaxSubscribers(this.maxSubscribers);
}
}

View File

@@ -19,7 +19,11 @@ package org.springframework.integration.config.xml;
import org.w3c.dom.Element;
import org.springframework.aop.scope.ScopedProxyUtils;
import org.springframework.beans.PropertyValue;
import org.springframework.beans.PropertyValues;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.TypedStringValue;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
@@ -85,4 +89,37 @@ public abstract class AbstractChannelParser extends AbstractBeanDefinitionParser
*/
protected abstract BeanDefinitionBuilder buildBeanDefinition(Element element, ParserContext parserContext);
protected void setMaxSubscribersProperty(ParserContext parserContext, BeanDefinitionBuilder builder, Element element, String channelInitializerPropertyName) {
String maxSubscribers = element.getAttribute("max-subscribers");
if (!StringUtils.hasText(maxSubscribers)) {
maxSubscribers = getDefaultMaxSubscribers(parserContext, channelInitializerPropertyName);
}
if (StringUtils.hasText(maxSubscribers)) {
builder.addPropertyValue("maxSubscribers", maxSubscribers);
}
}
protected String getDefaultMaxSubscribers(ParserContext parserContext, String channelInitializerPropertyName) {
String maxSubscribers = null;
BeanDefinition channelInitializer = parserContext.getRegistry().getBeanDefinition(
AbstractIntegrationNamespaceHandler.CHANNEL_INITIALIZER_BEAN_NAME);
if (channelInitializer != null) {
PropertyValues propertyValues = channelInitializer.getPropertyValues();
if (propertyValues != null) {
PropertyValue propertyValue = propertyValues
.getPropertyValue(channelInitializerPropertyName);
if (propertyValue != null) {
Object propertyValueValue = propertyValue.getValue();
if (propertyValueValue instanceof TypedStringValue) {
maxSubscribers = ((TypedStringValue) propertyValueValue).getValue();
}
else if (propertyValueValue instanceof String) {
maxSubscribers = (String) propertyValueValue;
}
}
}
}
return maxSubscribers;
}
}

View File

@@ -19,7 +19,6 @@ import java.util.Collection;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
@@ -41,11 +40,13 @@ import org.springframework.util.Assert;
* {@link AbstractIntegrationNamespaceHandler}.
*
* @author Oleg Zhurakousky
* @author Gary Russell
* @since 2.1.1
*/
final class ChannelInitializer implements BeanFactoryAware, InitializingBean {
public static String AUTO_CREATE_CHANNEL_CANDIDATES_BEAN_NAME = "$autoCreateChannelCandidates";
public static String CHANNEL_NAMES_ATTR = "channelNames";
private Log logger = LogFactory.getLog(this.getClass());
@@ -54,6 +55,10 @@ final class ChannelInitializer implements BeanFactoryAware, InitializingBean {
private volatile boolean autoCreate = true;
private volatile int defaultMaxUnicastSubscribers = Integer.MAX_VALUE;
private volatile int defaultMaxBroadcastSubscribers = Integer.MAX_VALUE;
public void setAutoCreate(boolean autoCreate) {
this.autoCreate = autoCreate;
}
@@ -62,6 +67,32 @@ final class ChannelInitializer implements BeanFactoryAware, InitializingBean {
this.beanFactory = beanFactory;
}
public int getDefaultMaxUnicastSubscribers() {
return defaultMaxUnicastSubscribers;
}
/**
* Set the default max-subscribers for all unicasting channels that don't have the
* attribute set on their dispatcher. Default {@link Integer#MAX_VALUE}.
* @param defaultMaxUnicastSubscribers
*/
public void setDefaultMaxUnicastSubscribers(int defaultMaxUnicastSubscribers) {
this.defaultMaxUnicastSubscribers = defaultMaxUnicastSubscribers;
}
public int getDefaultMaxBroadcastSubscribers() {
return defaultMaxBroadcastSubscribers;
}
/**
* Set the default max-subscribers for all broadcasting (pub-sub) channels that don't have the
* attribute set. Default {@link Integer#MAX_VALUE}.
* @param defaultMaxBroadcastSubscribers
*/
public void setDefaultMaxBroadcastSubscribers(int defaultMaxBroadcastSubscribers) {
this.defaultMaxBroadcastSubscribers = defaultMaxBroadcastSubscribers;
}
public void afterPropertiesSet() throws Exception {
Assert.notNull(this.beanFactory, "'beanFactory' must not be null");
if (!autoCreate){

View File

@@ -55,6 +55,20 @@ public abstract class IntegrationNamespaceUtils {
static final String EXPRESSION_ATTRIBUTE = "expression";
public static final String HANDLER_ALIAS_SUFFIX = ".handler";
/**
* Property name on ChannelInitializer used to configure the default max subscribers for
* unicast channels.
*/
public static String DEFAULT_MAX_UNICAST_SUBSCRIBERS_PROPERTY_NAME = "defaultMaxUnicastSubscribers";
/**
* Property name on ChannelInitializer used to configure the default max subscribers for
* broadcast channels.
*/
public static String DEFAULT_MAX_BROADCAST_SUBSCRIBERS_PROPERTY_NAME = "defaultMaxBroadcastSubscribers";
/**
* Configures the provided bean definition builder with a property value corresponding to the attribute whose name

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.
@@ -18,8 +18,6 @@ package org.springframework.integration.config.xml;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.w3c.dom.Element;
import org.springframework.beans.factory.config.TypedStringValue;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.ParserContext;
@@ -31,13 +29,15 @@ import org.springframework.integration.channel.RendezvousChannel;
import org.springframework.integration.store.MessageGroupQueue;
import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils;
import org.w3c.dom.Element;
/**
* Parser for the &lt;channel&gt; element.
*
*
* @author Mark Fisher
* @author Iwein Fuld
* @author Oleg Zhurakousky
* @author Gary Russell
*/
public class PointToPointChannelParser extends AbstractChannelParser {
@@ -116,11 +116,16 @@ public class PointToPointChannelParser extends AbstractChannelParser {
if ("failover".equals(dispatcherAttribute)) {
// round-robin dispatcher is used by default, the "failover" value simply disables it
builder.addConstructorArgValue(null);
}
}
}
else if (dispatcherElement == null) {
// configure the default DirectChannel with a RoundRobinLoadBalancingStrategy
builder = BeanDefinitionBuilder.genericBeanDefinition(DirectChannel.class);
String maxSubscribers = this.getDefaultMaxSubscribers(parserContext,
IntegrationNamespaceUtils.DEFAULT_MAX_UNICAST_SUBSCRIBERS_PROPERTY_NAME);
if (maxSubscribers != null) {
builder.addPropertyValue("maxSubscribers", maxSubscribers);
}
}
else {
// configure either an ExecutorChannel or DirectChannel based on existence of 'task-executor'
@@ -137,8 +142,10 @@ public class PointToPointChannelParser extends AbstractChannelParser {
String loadBalancer = dispatcherElement.getAttribute("load-balancer");
if ("none".equals(loadBalancer)) {
builder.addConstructorArgValue(null);
}
}
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, dispatcherElement, "failover");
this.setMaxSubscribersProperty(parserContext, builder, dispatcherElement,
IntegrationNamespaceUtils.DEFAULT_MAX_UNICAST_SUBSCRIBERS_PROPERTY_NAME);
}
return builder;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2009 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.
@@ -16,23 +16,24 @@
package org.springframework.integration.config.xml;
import org.w3c.dom.Element;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.integration.channel.PublishSubscribeChannel;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;
/**
* Parser for the &lt;publish-subscribe-channel&gt; element.
*
*
* @author Mark Fisher
* @author Gary Russell
*/
public class PublishSubscribeChannelParser extends AbstractChannelParser {
@Override
protected BeanDefinitionBuilder buildBeanDefinition(Element element, ParserContext parserContext) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(
IntegrationNamespaceUtils.BASE_PACKAGE + ".channel.PublishSubscribeChannel");
PublishSubscribeChannel.class);
String taskExecutorRef = element.getAttribute("task-executor");
if (StringUtils.hasText(taskExecutorRef)) {
builder.addConstructorArgReference(taskExecutorRef);
@@ -40,6 +41,8 @@ public class PublishSubscribeChannelParser extends AbstractChannelParser {
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "error-handler");
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "ignore-failures");
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "apply-sequence");
this.setMaxSubscribersProperty(parserContext, builder, element,
IntegrationNamespaceUtils.DEFAULT_MAX_BROADCAST_SUBSCRIBERS_PROPERTY_NAME);
return builder;
}

View File

@@ -20,7 +20,6 @@ import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.integration.core.MessageHandler;
import org.springframework.util.Assert;
@@ -43,9 +42,18 @@ public abstract class AbstractDispatcher implements MessageDispatcher {
protected final Log logger = LogFactory.getLog(this.getClass());
private volatile int maxSubscribers = Integer.MAX_VALUE;
private final OrderedAwareCopyOnWriteArraySet<MessageHandler> handlers =
new OrderedAwareCopyOnWriteArraySet<MessageHandler>();
/**
* Set the maximum subscribers allowed by this dispatcher.
* @param maxSubscribers
*/
public void setMaxSubscribers(int maxSubscribers) {
this.maxSubscribers = maxSubscribers;
}
/**
* Returns an unmodifiable {@link Set} of this dispatcher's handlers. This
@@ -62,6 +70,7 @@ public abstract class AbstractDispatcher implements MessageDispatcher {
*/
public boolean addHandler(MessageHandler handler) {
Assert.notNull(handler, "handler must not be null");
Assert.isTrue(this.handlers.size() < this.maxSubscribers, "Maximum subscribers exceeded");
return this.handlers.add(handler);
}

View File

@@ -302,6 +302,7 @@
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attributeGroup ref="subscribersAttributeGroup" />
</xsd:complexType>
<xsd:element name="publish-subscribe-channel">
@@ -388,6 +389,7 @@
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attributeGroup ref="subscribersAttributeGroup" />
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
@@ -3729,4 +3731,15 @@ endpoint itself is a Polling Consumer for a channel with a queue.
<xsd:attributeGroup ref="inputOutputChannelGroup"/>
</xsd:attributeGroup>
<xsd:attributeGroup name="subscribersAttributeGroup">
<xsd:attribute name="max-subscribers" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
Specifies the maximum subscribers allowed on this channel; defaults to
Integer.MAX_VALUE, unless a 'channelInitializer' bean has previously been
declared, with a different default.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:attributeGroup>
</xsd:schema>

View File

@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:int="http://www.springframework.org/schema/integration"
xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration-2.2.xsd
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.1.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<int:channel id="defaultChannel" />
<int:channel id="defaultChannel2">
<int:dispatcher />
</int:channel>
<int:channel id="explicitChannel">
<int:dispatcher max-subscribers="123" />
</int:channel>
<int:channel id="executorChannel">
<int:dispatcher task-executor="taskExecutor" />
</int:channel>
<int:channel id="explicitExecutorChannel">
<int:dispatcher task-executor="taskExecutor" max-subscribers="234" />
</int:channel>
<task:executor id="taskExecutor"/>
<int:publish-subscribe-channel id="pubSubDefaultChannel" />
<int:publish-subscribe-channel id="pubSubExplicitChannel" max-subscribers="2 "/>
</beans>

View File

@@ -0,0 +1,38 @@
/*
* 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.
* 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.config.xml;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @author Gary Russell
* @since 2.2
*
*/
@ContextConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
public class DispatcherMaxSubscribersDefaultConfigurationTests extends DispatcherMaxSubscribersTests {
@Test
public void test() {
doTestUnicast(Integer.MAX_VALUE, Integer.MAX_VALUE, 123, Integer.MAX_VALUE, 234);
doTestMulticast(Integer.MAX_VALUE, 2);
}
}

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- Explicit channel initializer, but no specification of the default max subscribers properties -->
<bean id="channelInitializer" class="org.springframework.integration.config.xml.ChannelInitializer">
<property name="autoCreate" value="true" />
</bean>
<import resource="DispatcherMaxSubscribersDefaultConfigurationTests-context.xml" />
</beans>

View File

@@ -0,0 +1,38 @@
/*
* 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.
* 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.config.xml;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @author Gary Russell
* @since 2.2
*
*/
@ContextConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
public class DispatcherMaxSubscribersDontOverrideDefaultTests extends DispatcherMaxSubscribersTests {
@Test
public void test() {
doTestUnicast(Integer.MAX_VALUE, Integer.MAX_VALUE, 123, Integer.MAX_VALUE, 234);
doTestMulticast(Integer.MAX_VALUE, 2);
}
}

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:int="http://www.springframework.org/schema/integration"
xsi:schemaLocation="http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration-2.2.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="channelInitializer" class="org.springframework.integration.config.xml.ChannelInitializer">
<property name="autoCreate" value="true" />
<property name="defaultMaxUnicastSubscribers" value="456" />
<property name="defaultMaxBroadcastSubscribers" value="789" />
</bean>
<import resource="DispatcherMaxSubscribersDefaultConfigurationTests-context.xml" />
<int:channel id="oneSub">
<int:dispatcher max-subscribers="1" />
</int:channel>
</beans>

View File

@@ -0,0 +1,66 @@
/*
* 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.
* 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.config.xml;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.integration.Message;
import org.springframework.integration.MessagingException;
import org.springframework.integration.core.MessageHandler;
import org.springframework.integration.core.SubscribableChannel;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @author Gary Russell
* @since 2.2
*
*/
@ContextConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
public class DispatcherMaxSubscribersOverrideDefaultTests extends DispatcherMaxSubscribersTests {
@Autowired
private SubscribableChannel oneSub;
@Test
public void test() {
this.doTestUnicast(456, 456, 123, 456, 234);
doTestMulticast(789, 2);
}
@Test
public void testExceed() {
oneSub.subscribe(new MessageHandler() {
public void handleMessage(Message<?> message) throws MessagingException {
}
});
try {
oneSub.subscribe(new MessageHandler() {
public void handleMessage(Message<?> message) throws MessagingException {
}
});
fail("Expected Exception");
}
catch (IllegalArgumentException e) {
assertEquals("Maximum subscribers exceeded", e.getMessage());
}
}
}

View File

@@ -0,0 +1,82 @@
/*
* 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.
* 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.config.xml;
import static org.junit.Assert.assertEquals;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.integration.MessageChannel;
import org.springframework.integration.test.util.TestUtils;
/**
* @author Gary Russell
* @since 2.2
*
*/
public abstract class DispatcherMaxSubscribersTests {
@Autowired
private MessageChannel defaultChannel;
@Autowired
private MessageChannel defaultChannel2;
@Autowired
private MessageChannel explicitChannel;
@Autowired
private MessageChannel executorChannel;
@Autowired
private MessageChannel explicitExecutorChannel;
@Autowired
private MessageChannel pubSubDefaultChannel;
@Autowired
private MessageChannel pubSubExplicitChannel;
public DispatcherMaxSubscribersTests() {
super();
}
protected void doTestUnicast(int val1, int val2, int val3, int val4, int val5) {
Integer defaultMax = TestUtils.getPropertyValue(
TestUtils.getPropertyValue(defaultChannel, "dispatcher"), "maxSubscribers", Integer.class);
assertEquals(val1, defaultMax.intValue());
Integer defaultMax2 = TestUtils.getPropertyValue(
TestUtils.getPropertyValue(defaultChannel2, "dispatcher"), "maxSubscribers", Integer.class);
assertEquals(val2, defaultMax2.intValue());
Integer explicitMax = TestUtils.getPropertyValue(
TestUtils.getPropertyValue(explicitChannel, "dispatcher"), "maxSubscribers", Integer.class);
assertEquals(val3, explicitMax.intValue());
Integer execMax = TestUtils.getPropertyValue(
TestUtils.getPropertyValue(executorChannel, "dispatcher"), "maxSubscribers", Integer.class);
assertEquals(val4, execMax.intValue());
Integer explicitExecMax = TestUtils.getPropertyValue(
TestUtils.getPropertyValue(explicitExecutorChannel, "dispatcher"), "maxSubscribers", Integer.class);
assertEquals(val5, explicitExecMax.intValue());
}
protected void doTestMulticast(int val1, int val2) {
Integer defaultMax = TestUtils.getPropertyValue(
TestUtils.getPropertyValue(pubSubDefaultChannel, "dispatcher"), "maxSubscribers", Integer.class);
assertEquals(val1, defaultMax.intValue());
Integer explicitMax = TestUtils.getPropertyValue(
TestUtils.getPropertyValue(pubSubExplicitChannel, "dispatcher"), "maxSubscribers", Integer.class);
assertEquals(val2, explicitMax.intValue());
}
}