INT-1069: Add namespace support for persistent queues
This commit is contained in:
@@ -37,10 +37,8 @@ public class PointToPointChannelParser extends AbstractChannelParser {
|
||||
|
||||
private static final String DISPATCHER_PACKAGE = IntegrationNamespaceUtils.BASE_PACKAGE + ".dispatcher";
|
||||
|
||||
|
||||
private final Log logger = LogFactory.getLog(this.getClass());
|
||||
|
||||
|
||||
@Override
|
||||
protected BeanDefinitionBuilder buildBeanDefinition(Element element, ParserContext parserContext) {
|
||||
BeanDefinitionBuilder builder = null;
|
||||
@@ -49,11 +47,20 @@ public class PointToPointChannelParser extends AbstractChannelParser {
|
||||
// configure a queue-based channel if any queue sub-element is defined
|
||||
if ((queueElement = DomUtils.getChildElementByTagName(element, "queue")) != null) {
|
||||
builder = BeanDefinitionBuilder.genericBeanDefinition(CHANNEL_PACKAGE + ".QueueChannel");
|
||||
boolean hasCapacity = this.parseQueueCapacity(builder, queueElement);
|
||||
boolean hasStoreRef = this.parseStoreRef(builder, queueElement, element.getAttribute(ID_ATTRIBUTE));
|
||||
boolean hasQueueRef = this.parseQueueRef(builder, queueElement);
|
||||
if (hasCapacity && hasQueueRef) {
|
||||
parserContext.getReaderContext().error("The 'capacity' attribute is not allowed" +
|
||||
" when providing a 'ref' to a custom queue.", element);
|
||||
if (!hasStoreRef) {
|
||||
boolean hasCapacity = this.parseQueueCapacity(builder, queueElement);
|
||||
if (hasCapacity && hasQueueRef) {
|
||||
parserContext.getReaderContext().error(
|
||||
"The 'capacity' attribute is not allowed" + " when providing a 'ref' to a custom queue.",
|
||||
element);
|
||||
}
|
||||
}
|
||||
if (hasStoreRef && hasQueueRef) {
|
||||
parserContext.getReaderContext().error(
|
||||
"The 'message-store' attribute is not allowed" + " when providing a 'ref' to a custom queue.",
|
||||
element);
|
||||
}
|
||||
}
|
||||
else if ((queueElement = DomUtils.getChildElementByTagName(element, "priority-queue")) != null) {
|
||||
@@ -74,14 +81,15 @@ public class PointToPointChannelParser extends AbstractChannelParser {
|
||||
String dispatcherAttribute = element.getAttribute("dispatcher");
|
||||
boolean hasDispatcherAttribute = StringUtils.hasText(dispatcherAttribute);
|
||||
if (hasDispatcherAttribute && logger.isWarnEnabled()) {
|
||||
logger.warn("The 'dispatcher' attribute on the 'channel' element is deprecated. " +
|
||||
"Please use the 'dispatcher' sub-element instead.");
|
||||
logger.warn("The 'dispatcher' attribute on the 'channel' element is deprecated. "
|
||||
+ "Please use the 'dispatcher' sub-element instead.");
|
||||
}
|
||||
|
||||
// verify that a dispatcher is not provided if a queue sub-element exists
|
||||
if (queueElement != null && (dispatcherElement != null || hasDispatcherAttribute)) {
|
||||
parserContext.getReaderContext().error("The 'dispatcher' attribute or sub-element " +
|
||||
"and any queue sub-element are mutually exclusive.", element);
|
||||
parserContext.getReaderContext().error(
|
||||
"The 'dispatcher' attribute or sub-element " + "and any queue sub-element are mutually exclusive.",
|
||||
element);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -90,9 +98,10 @@ public class PointToPointChannelParser extends AbstractChannelParser {
|
||||
}
|
||||
|
||||
if (dispatcherElement != null && hasDispatcherAttribute) {
|
||||
parserContext.getReaderContext().error("The 'dispatcher' attribute and 'dispatcher' " +
|
||||
"sub-element are mutually exclusive. NOTE: the attribute is DEPRECATED. " +
|
||||
"Please use the dispatcher sub-element instead.", element);
|
||||
parserContext.getReaderContext().error(
|
||||
"The 'dispatcher' attribute and 'dispatcher' "
|
||||
+ "sub-element are mutually exclusive. NOTE: the attribute is DEPRECATED. "
|
||||
+ "Please use the dispatcher sub-element instead.", element);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -102,15 +111,15 @@ public class PointToPointChannelParser extends AbstractChannelParser {
|
||||
builder = BeanDefinitionBuilder.genericBeanDefinition(CHANNEL_PACKAGE + ".DirectChannel");
|
||||
if (!"failover".equals(dispatcherAttribute)) {
|
||||
// round-robin dispatcher is used by default, the "failover" value simply disables it
|
||||
builder.addConstructorArgValue(new RootBeanDefinition(
|
||||
DISPATCHER_PACKAGE + ".RoundRobinLoadBalancingStrategy", null, null));
|
||||
builder.addConstructorArgValue(new RootBeanDefinition(DISPATCHER_PACKAGE
|
||||
+ ".RoundRobinLoadBalancingStrategy", null, null));
|
||||
}
|
||||
}
|
||||
else if (dispatcherElement == null) {
|
||||
// configure the default DirectChannel with a RoundRobinLoadBalancingStrategy
|
||||
builder = BeanDefinitionBuilder.genericBeanDefinition(CHANNEL_PACKAGE + ".DirectChannel");
|
||||
builder.addConstructorArgValue(new RootBeanDefinition(
|
||||
DISPATCHER_PACKAGE + ".RoundRobinLoadBalancingStrategy", null, null));
|
||||
builder.addConstructorArgValue(new RootBeanDefinition(DISPATCHER_PACKAGE
|
||||
+ ".RoundRobinLoadBalancingStrategy", null, null));
|
||||
}
|
||||
else {
|
||||
// configure either an ExecutorChannel or DirectChannel based on existence of 'task-executor'
|
||||
@@ -126,8 +135,8 @@ public class PointToPointChannelParser extends AbstractChannelParser {
|
||||
// configure the default RoundRobinLoadBalancingStrategy
|
||||
String loadBalancer = dispatcherElement.getAttribute("load-balancer");
|
||||
if (!"none".equals(loadBalancer)) {
|
||||
builder.addConstructorArgValue(new RootBeanDefinition(
|
||||
DISPATCHER_PACKAGE + ".RoundRobinLoadBalancingStrategy", null, null));
|
||||
builder.addConstructorArgValue(new RootBeanDefinition(DISPATCHER_PACKAGE
|
||||
+ ".RoundRobinLoadBalancingStrategy", null, null));
|
||||
}
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, dispatcherElement, "failover");
|
||||
}
|
||||
@@ -145,11 +154,25 @@ public class PointToPointChannelParser extends AbstractChannelParser {
|
||||
|
||||
private boolean parseQueueRef(BeanDefinitionBuilder builder, Element queueElement) {
|
||||
String queueRef = queueElement.getAttribute("ref");
|
||||
if (StringUtils.hasText(queueRef)){
|
||||
if (StringUtils.hasText(queueRef)) {
|
||||
builder.addConstructorArgReference(queueRef);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean parseStoreRef(BeanDefinitionBuilder builder, Element queueElement, String channel) {
|
||||
String storeRef = queueElement.getAttribute("message-store");
|
||||
if (StringUtils.hasText(storeRef)) {
|
||||
BeanDefinitionBuilder queueBuilder = BeanDefinitionBuilder
|
||||
.genericBeanDefinition(IntegrationNamespaceUtils.BASE_PACKAGE + ".store.MessageGroupQueue");
|
||||
queueBuilder.addConstructorArgReference(storeRef);
|
||||
queueBuilder.addConstructorArgValue(channel);
|
||||
parseQueueCapacity(queueBuilder, queueElement);
|
||||
builder.addConstructorArgValue(queueBuilder.getBeanDefinition());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -139,12 +139,15 @@ public class MessageGroupQueue extends AbstractQueue<Message<?>> implements Bloc
|
||||
}
|
||||
|
||||
public boolean offer(Message<?> e, long timeout, TimeUnit unit) throws InterruptedException {
|
||||
if (!offer(e)) {
|
||||
long threshold = System.currentTimeMillis() + TimeUnit.MILLISECONDS.convert(timeout, unit);
|
||||
boolean result = offer(e);
|
||||
while (!result && System.currentTimeMillis() < threshold) {
|
||||
synchronized (writeLock) {
|
||||
writeLock.wait(TimeUnit.MILLISECONDS.convert(timeout, unit));
|
||||
writeLock.wait(threshold - System.currentTimeMillis());
|
||||
}
|
||||
result = offer(e);
|
||||
}
|
||||
return offer(e);
|
||||
return result;
|
||||
}
|
||||
|
||||
public Message<?> poll(long timeout, TimeUnit unit) throws InterruptedException {
|
||||
|
||||
@@ -164,9 +164,7 @@ public class SimpleMessageGroup implements MessageGroup {
|
||||
|
||||
public void markAll() {
|
||||
synchronized (lock) {
|
||||
marked.addAll(unmarked);
|
||||
unmarked.clear();
|
||||
// unmarked.drainTo(marked);
|
||||
unmarked.drainTo(marked);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,23 +1,23 @@
|
||||
<?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
|
||||
http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/integration
|
||||
http://www.springframework.org/schema/integration/spring-integration.xsd">
|
||||
|
||||
<channel id="output">
|
||||
<queue capacity="5" message-store="messageStore" />
|
||||
</channel>
|
||||
|
||||
<channel id="input" />
|
||||
|
||||
<service-activator id="activator" ref="bean" input-channel="input" output-channel="output"/>
|
||||
|
||||
<beans:bean id="messageStore" class="org.springframework.integration.store.SimpleMessageStore" />
|
||||
|
||||
<beans:bean id="bean" class="org.springframework.integration.config.TestHandler">
|
||||
<beans:property name="replyMessageText" value="hello"/>
|
||||
</beans:bean>
|
||||
|
||||
</beans:beans>
|
||||
<?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
|
||||
http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/integration
|
||||
http://www.springframework.org/schema/integration/spring-integration.xsd">
|
||||
|
||||
<channel id="output">
|
||||
<queue capacity="5" message-store="messageStore" />
|
||||
</channel>
|
||||
|
||||
<channel id="input" />
|
||||
|
||||
<service-activator id="activator" ref="bean" input-channel="input" output-channel="output"/>
|
||||
|
||||
<beans:bean id="messageStore" class="org.springframework.integration.store.SimpleMessageStore" />
|
||||
|
||||
<beans:bean id="bean" class="org.springframework.integration.config.TestHandler">
|
||||
<beans:property name="replyMessageText" value="hello"/>
|
||||
</beans:bean>
|
||||
|
||||
</beans:beans>
|
||||
|
||||
@@ -56,16 +56,17 @@ public class ChannelWithMessageStoreParserTests {
|
||||
|
||||
@Test
|
||||
@DirtiesContext
|
||||
public void testAggregation() throws Exception {
|
||||
public void testActivatorSendsToPersistentQueue() throws Exception {
|
||||
|
||||
input.send(createMessage("123", "id1", 3, 1, null));
|
||||
assertEquals(1, messageGroupStore.getMessageGroup("id1").size());
|
||||
handler.getLatch().await(100, TimeUnit.MILLISECONDS);
|
||||
assertEquals("The message payload is not correct", "123", handler.getMessageString());
|
||||
assertEquals(0, messageGroupStore.getMessageGroup("id1").size());
|
||||
// The group id for buffered messages is the channel name
|
||||
assertEquals(1, messageGroupStore.getMessageGroup("output").size());
|
||||
|
||||
Message<?> result = output.receive(100);
|
||||
assertEquals("hello", result.getPayload());
|
||||
assertEquals(0, messageGroupStore.getMessageGroup("output").size());
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
<?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
|
||||
http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/integration
|
||||
http://www.springframework.org/schema/integration/spring-integration.xsd">
|
||||
|
||||
<channel id="output">
|
||||
<queue message-store="messageStore" ref="queue" />
|
||||
</channel>
|
||||
|
||||
<beans:bean id="messageStore" class="org.springframework.integration.store.SimpleMessageStore" />
|
||||
|
||||
</beans:beans>
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2002-2010 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;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
import org.mockito.Matchers;
|
||||
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
|
||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public class InvalidChannelWithMessageStoreParserTests {
|
||||
|
||||
@Rule
|
||||
public ExpectedException exception = ExpectedException.none();
|
||||
|
||||
@Test
|
||||
public void testRefAndStoreIllegal() throws Exception {
|
||||
exception.expect(BeanDefinitionParsingException.class);
|
||||
exception.expectMessage(Matchers.contains("'message-store' attribute is not allowed"));
|
||||
new ClassPathXmlApplicationContext(getClass().getSimpleName() + "-context.xml", getClass());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -50,7 +50,7 @@ public class TestHandler {
|
||||
public String handle(Message<?> message) {
|
||||
this.messageString = message.getPayload().toString();
|
||||
this.latch.countDown();
|
||||
return (this.replyMessageText != null) ? this.replyMessageText : null;
|
||||
return this.replyMessageText;
|
||||
}
|
||||
|
||||
public String getMessageString() {
|
||||
|
||||
Reference in New Issue
Block a user