INT-3230 Added support for 'load-balancer-ref' attribute

of the 'channel' element. Added tests and updated documentation

INT-3230 addressed PR comments
This commit is contained in:
Oleg Zhurakousky
2013-12-11 20:42:38 +02:00
committed by Artem Bilan
parent d39b6870c4
commit 52f5f4ad77
7 changed files with 119 additions and 23 deletions

View File

@@ -16,8 +16,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;
@@ -43,8 +41,6 @@ import org.springframework.util.xml.DomUtils;
*/
public class PointToPointChannelParser extends AbstractChannelParser {
private final Log logger = LogFactory.getLog(this.getClass());
@Override
protected BeanDefinitionBuilder buildBeanDefinition(Element element, ParserContext parserContext) {
BeanDefinitionBuilder builder = null;
@@ -108,12 +104,22 @@ public class PointToPointChannelParser extends AbstractChannelParser {
else {
builder = BeanDefinitionBuilder.genericBeanDefinition(DirectChannel.class);
}
// unless the 'load-balancer' attribute is explicitly set to 'none',
// unless the 'load-balancer' attribute is explicitly set to 'none' or 'load-balancer-ref' is explicitly configured,
// configure the default RoundRobinLoadBalancingStrategy
String loadBalancer = dispatcherElement.getAttribute("load-balancer");
if ("none".equals(loadBalancer)) {
builder.addConstructorArgValue(null);
String loadBalancerRef = dispatcherElement.getAttribute("load-balancer-ref");
if (StringUtils.hasText(loadBalancer) && StringUtils.hasText(loadBalancerRef)){
parserContext.getReaderContext().error("'load-balancer' and 'load-balancer-ref' are mutually exclusive", element);
}
if (StringUtils.hasText(loadBalancerRef)){
builder.addConstructorArgReference(loadBalancerRef);
}
else {
if ("none".equals(loadBalancer)) {
builder.addConstructorArgValue(null);
}
}
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, dispatcherElement, "failover");
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, dispatcherElement, "max-subscribers");
}

View File

@@ -286,12 +286,25 @@
(i.e. one without a queue).
</xsd:documentation>
</xsd:annotation>
<xsd:attribute name="load-balancer-ref" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.integration.dispatcher.LoadBalancingStrategy" />
</tool:annotation>
</xsd:appinfo>
<xsd:documentation>
A reference to a bean that implements the 'org.springframework.integration.dispatcher.LoadBalancingStrategy'.
This attribute is mutually exclusive with 'load-balancer'.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="load-balancer">
<xsd:annotation>
<xsd:documentation>
Defines a load-balancing strategy for the channel's dispatcher.
The default is a round-robin load
balancer.
The default is a round-robin load balancer.
This attribute is mutually exclusive with 'load-balancer-ref'.
</xsd:documentation>
</xsd:annotation>
<xsd:simpleType>

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"
xmlns:int="http://www.springframework.org/schema/integration"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd">
<int:channel id="myChannel">
<int:dispatcher load-balancer-ref="lb2" load-balancer="round-robin"/>
</int:channel>
<bean id="lb"
class="org.springframework.integration.channel.config.DispatchingChannelParserTests.SampleLoadBalancingStrategy"/>
</beans>

View File

@@ -1,7 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/integration"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration
http://www.springframework.org/schema/integration/spring-integration.xsd">
@@ -30,7 +31,13 @@
<dispatcher load-balancer="round-robin" task-executor="taskExecutor"/>
</channel>
<beans:bean id="taskExecutor"
class="org.springframework.core.task.SimpleAsyncTaskExecutor" />
<channel id="lbRefChannel">
<dispatcher load-balancer-ref="lb"/>
</channel>
<beans:bean id="taskExecutor"
class="org.springframework.core.task.SimpleAsyncTaskExecutor"/>
<beans:bean id="lb"
class="org.springframework.integration.channel.config.DispatchingChannelParserTests.SampleLoadBalancingStrategy"/>
</beans:beans>

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2009 the original author or authors.
* Copyright 2002-2013 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.
@@ -20,26 +20,37 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
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 java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import org.hamcrest.Matchers;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.DirectFieldAccessor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.integration.Message;
import org.springframework.integration.MessageChannel;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.integration.channel.ExecutorChannel;
import org.springframework.integration.core.MessageHandler;
import org.springframework.integration.dispatcher.LoadBalancingStrategy;
import org.springframework.integration.dispatcher.RoundRobinLoadBalancingStrategy;
import org.springframework.integration.test.util.TestUtils;
import org.springframework.integration.util.ErrorHandlingTaskExecutor;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @author Mark Fisher
* @author Oleg Zhurakousky
* @since 1.0.3
*/
@RunWith(SpringJUnit4ClassRunner.class)
@@ -116,6 +127,24 @@ public class DispatchingChannelParserTests {
new DirectFieldAccessor(executor).getPropertyValue("executor"));
}
@Test
public void loadBalancerRef() {
MessageChannel channel = channels.get("lbRefChannel");
LoadBalancingStrategy lbStrategy = TestUtils.getPropertyValue(channel, "dispatcher.loadBalancingStrategy", LoadBalancingStrategy.class);
assertTrue(lbStrategy instanceof SampleLoadBalancingStrategy);
}
@Test
public void loadBalancerRefFailWithLoadBalancer() {
try {
new ClassPathXmlApplicationContext("ChannelWithLoadBalancerRef-fail-config.xml", this.getClass());
}
catch (BeanDefinitionParsingException e) {
assertThat(e.getMessage(), Matchers.containsString("'load-balancer' and 'load-balancer-ref' are mutually exclusive"));
}
}
private static Object getDispatcherProperty(String propertyName, MessageChannel channel) {
return new DirectFieldAccessor(
@@ -123,4 +152,11 @@ public class DispatchingChannelParserTests {
.getPropertyValue(propertyName);
}
public static class SampleLoadBalancingStrategy implements LoadBalancingStrategy {
@Override
public Iterator<MessageHandler> getHandlerIterator(Message<?> message, Collection<MessageHandler> handlers) {
return handlers.iterator();
}
}
}

View File

@@ -199,17 +199,27 @@
</para>
<para>
The <classname>DirectChannel</classname> internally delegates to a Message Dispatcher to invoke its
subscribed Message Handlers, and that dispatcher can have a load-balancing strategy. The load-balancer
determines how invocations will be ordered in the case that there are multiple handlers subscribed to the
same channel. When using the namespace support described below, the default strategy is
"round-robin" which essentially load-balances across the handlers in rotation.
<note>
The "round-robin" strategy is currently the only implementation available out-of-the-box in Spring
Integration. Other strategy implementations may be added in future versions.
</note>
subscribed Message Handlers, and that dispatcher can have a load-balancing strategy exposed via
<emphasis>load-balancer</emphasis> or <emphasis>load-balancer-ref</emphasis> attributes (mutually exclusive). The load balancing strategy
is used by the Message Dispatcher to help determine how Messages are distributed amongst Message Handlers
in the case that there are multiple Message Handlers subscribed to the same channel.
As a convinience the <emphasis>load-balancer</emphasis> attribute exposes enumeration of values pointing to pre-existing implementations
of <classname>LoadBalancingStrategy</classname>.
The "round-robin" (load-balances across the handlers in rotation) and "none" (for the cases where one wants to explicitely disable load balancing)
are the only available values.
Other strategy implementations may be added in future versions.
However, since version 3.0 you can provide your own implementation of the <classname>LoadBalancingStrategy</classname> and
inject it using <emphasis>load-balancer-ref</emphasis> attribute which should point to a bean that implements
<classname>LoadBalancingStrategy</classname>.
<programlisting language="xml"><![CDATA[<int:channel id="lbRefChannel">
<int:dispatcher load-balancer-ref="lb"/>
</int:channel>
<bean id="lb" class="foo.bar.SampleLoadBalancingStrategy"/>]]></programlisting>
Note that <emphasis>load-balancer</emphasis> or <emphasis>load-balancer-ref</emphasis> attributes are mutually exclusive.
</para>
<para>
The load-balancer also works in combination with a boolean <emphasis>failover</emphasis> property.
The load-balancing also works in combination with a boolean <emphasis>failover</emphasis> property.
If the "failover" value is true (the default), then the dispatcher will fall back to any subsequent
handlers as necessary when preceding handlers throw Exceptions. The order is determined by an optional
order value defined on the handlers themselves or, if no such value exists, the order in which the

View File

@@ -620,6 +620,16 @@
least 1.
</para>
</section>
<section id="3.0-direct-channel-lb-ref">
<title>Direct Channel Load Balancing configuration</title>
<para>
Previously, when configuring <classname>LoadBalancingStrategy</classname> on the channel's 'dispatcher' sub-element
the only available option was to use a pre-defined enumeration of values which did not allow one to set a custom implementation
of the <classname>LoadBalancingStrategy</classname>. Starting with v3.0 you can now use 'load-balancer-ref' to provide
a reference to a custom implementation of the <classname>LoadBalancingStrategy</classname>.
For more information see <xref linkend="channel-implementations-directchannel"/>.
</para>
</section>
<section id="3.0-jpa-first-result">
<title>JPA Adapters: first-result attribute</title>
<para>