diff --git a/spring-integration-core/src/main/java/org/springframework/integration/annotation/CompletionStrategy.java b/spring-integration-core/src/main/java/org/springframework/integration/annotation/CompletionStrategy.java
new file mode 100644
index 0000000000..4844b37864
--- /dev/null
+++ b/spring-integration-core/src/main/java/org/springframework/integration/annotation/CompletionStrategy.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2002-2008 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.annotation;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Indicates that a method is capable of asserting if a list of messages or
+ * payload objects is complete.
+ *
+ * @author Marius Bogoevici
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+@Documented
+public @interface CompletionStrategy {
+
+}
diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/AggregatorParser.java b/spring-integration-core/src/main/java/org/springframework/integration/config/AggregatorParser.java
index 060f8153ee..1a318e040c 100644
--- a/spring-integration-core/src/main/java/org/springframework/integration/config/AggregatorParser.java
+++ b/spring-integration-core/src/main/java/org/springframework/integration/config/AggregatorParser.java
@@ -16,8 +16,6 @@
package org.springframework.integration.config;
-import org.w3c.dom.Element;
-
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
@@ -27,7 +25,10 @@ import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.integration.ConfigurationException;
import org.springframework.integration.router.AggregatingMessageHandler;
import org.springframework.integration.router.AggregatorAdapter;
+import org.springframework.integration.router.CompletionStrategyAdapter;
import org.springframework.util.StringUtils;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
/**
* Parser for the aggregator element of the integration namespace.
@@ -75,6 +76,9 @@ public class AggregatorParser implements BeanDefinitionParser {
public static final String TIMEOUT = "timeout";
+ public static final String AGGREGATOR_ELEMENT = "aggregator";
+
+ public static final String COMPLETION_STRATEGY_ELEMENT = "completion-strategy";
public BeanDefinition parse(Element element, ParserContext parserContext) {
return parseAggregatorElement(element, parserContext, true);
@@ -86,6 +90,8 @@ public class AggregatorParser implements BeanDefinitionParser {
final String id = element.getAttribute(ID_ATTRIBUTE);
final String ref = element.getAttribute(REF_ATTRIBUTE);
final String method = element.getAttribute(METHOD_ATTRIBUTE);
+ final String completionStrategyRef = element.getAttribute(COMPLETION_STRATEGY_ATTRIBUTE);
+ final NodeList completionStrategyChildElements = element.getElementsByTagName(COMPLETION_STRATEGY_ELEMENT);
if (!StringUtils.hasText(ref)) {
throw new ConfigurationException("The 'ref' attribute must be present");
}
@@ -94,19 +100,36 @@ public class AggregatorParser implements BeanDefinitionParser {
"The 'id' attribute is only supported for top-level elements.",
parserContext.extractSource(element));
}
+ if (completionStrategyChildElements.getLength() > 0 && StringUtils.hasText(completionStrategyRef)) {
+ parserContext
+ .getReaderContext()
+ .error(
+ "The 'completion-strategy' element is only supported when no 'completion-strategy' attribute is specified.",
+ parserContext.extractSource(element));
+ }
if (!StringUtils.hasText(method)) {
aggregatorDef.getConstructorArgumentValues().addGenericArgumentValue(new RuntimeBeanReference(ref));
}
else {
- BeanDefinition adapterDefinition = new RootBeanDefinition(AggregatorAdapter.class);
- adapterDefinition.getConstructorArgumentValues().addGenericArgumentValue(new RuntimeBeanReference(ref));
- adapterDefinition.getConstructorArgumentValues().addGenericArgumentValue(method);
- String adapterBeanName = parserContext.getReaderContext().generateBeanName(adapterDefinition);
- parserContext.registerBeanComponent(new BeanComponentDefinition(adapterDefinition, adapterBeanName));
- aggregatorDef.getConstructorArgumentValues().addGenericArgumentValue(new RuntimeBeanReference(adapterBeanName));
+ String adapterBeanName = createAdapterAndReturnBeanName(parserContext, ref, method, AggregatorAdapter.class);
+ aggregatorDef.getConstructorArgumentValues().addGenericArgumentValue(
+ new RuntimeBeanReference(adapterBeanName));
}
- IntegrationNamespaceUtils.setBeanReferenceIfAttributeDefined(aggregatorDef, COMPLETION_STRATEGY_PROPERTY,
- element, COMPLETION_STRATEGY_ATTRIBUTE);
+
+ if (StringUtils.hasText(completionStrategyRef)) {
+ aggregatorDef.getPropertyValues().addPropertyValue(COMPLETION_STRATEGY_PROPERTY,
+ new RuntimeBeanReference(completionStrategyRef));
+ }
+ else if (completionStrategyChildElements.getLength() > 0) {
+ Element completionStrategyElement = (Element) completionStrategyChildElements.item(0);
+ String childCompletionStrategyReference = completionStrategyElement.getAttribute(REF_ATTRIBUTE);
+ String childCompletionStrategyMethod = completionStrategyElement.getAttribute(METHOD_ATTRIBUTE);
+ String adapterBeanName = createAdapterAndReturnBeanName(parserContext, childCompletionStrategyReference,
+ childCompletionStrategyMethod, CompletionStrategyAdapter.class);
+ aggregatorDef.getPropertyValues().addPropertyValue(COMPLETION_STRATEGY_PROPERTY,
+ new RuntimeBeanReference(adapterBeanName));
+ }
+
IntegrationNamespaceUtils.setBeanReferenceIfAttributeDefined(aggregatorDef, DEFAULT_REPLY_CHANNEL_PROPERTY,
element, DEFAULT_REPLY_CHANNEL_ATTRIBUTE);
IntegrationNamespaceUtils.setBeanReferenceIfAttributeDefined(aggregatorDef, DISCARD_CHANNEL_PROPERTY, element,
@@ -126,4 +149,14 @@ public class AggregatorParser implements BeanDefinitionParser {
return aggregatorDef;
}
+ private String createAdapterAndReturnBeanName(ParserContext parserContext, final String ref, final String method,
+ Class> adapterClass) {
+ BeanDefinition adapterDefinition = new RootBeanDefinition(adapterClass);
+ adapterDefinition.getConstructorArgumentValues().addGenericArgumentValue(new RuntimeBeanReference(ref));
+ adapterDefinition.getConstructorArgumentValues().addGenericArgumentValue(method);
+ String adapterBeanName = parserContext.getReaderContext().generateBeanName(adapterDefinition);
+ parserContext.registerBeanComponent(new BeanComponentDefinition(adapterDefinition, adapterBeanName));
+ return adapterBeanName;
+ }
+
}
diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/MessageEndpointAnnotationPostProcessor.java b/spring-integration-core/src/main/java/org/springframework/integration/config/MessageEndpointAnnotationPostProcessor.java
index 4017572a94..35062f1b95 100644
--- a/spring-integration-core/src/main/java/org/springframework/integration/config/MessageEndpointAnnotationPostProcessor.java
+++ b/spring-integration-core/src/main/java/org/springframework/integration/config/MessageEndpointAnnotationPostProcessor.java
@@ -26,7 +26,6 @@ import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
-
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
@@ -38,7 +37,15 @@ import org.springframework.integration.adapter.DefaultTargetAdapter;
import org.springframework.integration.adapter.MethodInvokingSource;
import org.springframework.integration.adapter.MethodInvokingTarget;
import org.springframework.integration.adapter.PollingSourceAdapter;
-import org.springframework.integration.annotation.*;
+import org.springframework.integration.annotation.Aggregator;
+import org.springframework.integration.annotation.CompletionStrategy;
+import org.springframework.integration.annotation.Concurrency;
+import org.springframework.integration.annotation.DefaultOutput;
+import org.springframework.integration.annotation.Handler;
+import org.springframework.integration.annotation.MessageEndpoint;
+import org.springframework.integration.annotation.Polled;
+import org.springframework.integration.annotation.Router;
+import org.springframework.integration.annotation.Splitter;
import org.springframework.integration.bus.MessageBus;
import org.springframework.integration.channel.ChannelRegistryAware;
import org.springframework.integration.channel.MessageChannel;
@@ -50,9 +57,11 @@ import org.springframework.integration.handler.MessageHandlerChain;
import org.springframework.integration.handler.config.DefaultMessageHandlerCreator;
import org.springframework.integration.handler.config.MessageHandlerCreator;
import org.springframework.integration.message.Message;
+import org.springframework.integration.router.AggregatingMessageHandler;
+import org.springframework.integration.router.CompletionStrategyAdapter;
+import org.springframework.integration.router.config.AggregatorMessageHandlerCreator;
import org.springframework.integration.router.config.RouterMessageHandlerCreator;
import org.springframework.integration.router.config.SplitterMessageHandlerCreator;
-import org.springframework.integration.router.config.AggregatorMessageHandlerCreator;
import org.springframework.integration.scheduling.PollingSchedule;
import org.springframework.integration.scheduling.Subscription;
import org.springframework.util.Assert;
@@ -64,25 +73,22 @@ import org.springframework.util.StringUtils;
* classes annotated with {@link MessageEndpoint @MessageEndpoint}.
*
* @author Mark Fisher
+ * @author Marius Bogoevici
*/
public class MessageEndpointAnnotationPostProcessor implements BeanPostProcessor, InitializingBean {
private final Log logger = LogFactory.getLog(this.getClass());
- private final Map, MessageHandlerCreator> handlerCreators =
- new ConcurrentHashMap, MessageHandlerCreator>();
+ private final Map, MessageHandlerCreator> handlerCreators = new ConcurrentHashMap, MessageHandlerCreator>();
private final MessageBus messageBus;
-
public MessageEndpointAnnotationPostProcessor(MessageBus messageBus) {
Assert.notNull(messageBus, "'messageBus' must not be null");
this.messageBus = messageBus;
}
-
- public void setCustomHandlerCreators(
- Map, MessageHandlerCreator> customHandlerCreators) {
+ public void setCustomHandlerCreators(Map, MessageHandlerCreator> customHandlerCreators) {
for (Map.Entry, MessageHandlerCreator> entry : customHandlerCreators.entrySet()) {
this.handlerCreators.put(entry.getKey(), entry.getValue());
}
@@ -114,8 +120,8 @@ public class MessageEndpointAnnotationPostProcessor implements BeanPostProcessor
this.configureDefaultOutput(bean, beanName, endpointAnnotation, endpoint);
Concurrency concurrencyAnnotation = AnnotationUtils.findAnnotation(beanClass, Concurrency.class);
if (concurrencyAnnotation != null) {
- ConcurrencyPolicy concurrencyPolicy = new ConcurrencyPolicy(
- concurrencyAnnotation.coreSize(), concurrencyAnnotation.maxSize());
+ ConcurrencyPolicy concurrencyPolicy = new ConcurrencyPolicy(concurrencyAnnotation.coreSize(),
+ concurrencyAnnotation.maxSize());
concurrencyPolicy.setKeepAliveSeconds(concurrencyAnnotation.keepAliveSeconds());
concurrencyPolicy.setQueueCapacity(concurrencyAnnotation.queueCapacity());
endpoint.setConcurrencyPolicy(concurrencyPolicy);
@@ -127,6 +133,7 @@ public class MessageEndpointAnnotationPostProcessor implements BeanPostProcessor
}
});
}
+ this.configureCompletionStrategy(bean, endpoint);
this.messageBus.registerEndpoint(beanName + "-endpoint", endpoint);
return bean;
}
@@ -166,8 +173,8 @@ public class MessageEndpointAnnotationPostProcessor implements BeanPostProcessor
});
}
- private void configureDefaultOutput(final Object bean, final String beanName,
- final MessageEndpoint annotation, final DefaultMessageEndpoint endpoint) {
+ private void configureDefaultOutput(final Object bean, final String beanName, final MessageEndpoint annotation,
+ final DefaultMessageEndpoint endpoint) {
String channelName = annotation.defaultOutput();
if (StringUtils.hasText(channelName)) {
endpoint.setDefaultOutputChannelName(channelName);
@@ -175,6 +182,7 @@ public class MessageEndpointAnnotationPostProcessor implements BeanPostProcessor
}
ReflectionUtils.doWithMethods(this.getBeanClass(bean), new ReflectionUtils.MethodCallback() {
boolean foundDefaultOutput = false;
+
public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
Annotation annotation = AnnotationUtils.getAnnotation(method, DefaultOutput.class);
if (annotation != null) {
@@ -205,6 +213,38 @@ public class MessageEndpointAnnotationPostProcessor implements BeanPostProcessor
});
}
+ private void configureCompletionStrategy(final Object bean, final DefaultMessageEndpoint endpoint) {
+ ReflectionUtils.doWithMethods(bean.getClass(), new ReflectionUtils.MethodCallback() {
+ public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
+ Annotation annotation = AnnotationUtils.getAnnotation(method, CompletionStrategy.class);
+ if (annotation != null) {
+ final MessageHandler endpointHandler = endpoint.getHandler();
+ AggregatingMessageHandler aggregatingMessageHandler = null;
+ if (endpointHandler != null) {
+ if (endpointHandler instanceof MessageHandlerChain) {
+ for (MessageHandler handlerInChain : ((MessageHandlerChain) endpointHandler).getHandlers()) {
+ if (handlerInChain instanceof AggregatingMessageHandler) {
+ aggregatingMessageHandler = (AggregatingMessageHandler) handlerInChain;
+ break;
+ }
+ }
+ }
+ else if (endpointHandler instanceof AggregatingMessageHandler) {
+ aggregatingMessageHandler = (AggregatingMessageHandler) endpointHandler;
+ }
+ }
+ if (aggregatingMessageHandler == null) {
+ throw new ConfigurationException(
+ "@CompletionStrategy supported only when @Aggregator is present");
+ }
+ else {
+ aggregatingMessageHandler.setCompletionStrategy(new CompletionStrategyAdapter(bean, method));
+ }
+ }
+ }
+ });
+ }
+
@SuppressWarnings("unchecked")
private MessageHandlerChain createHandlerChain(final Object bean) {
final List handlers = new ArrayList();
@@ -217,8 +257,8 @@ public class MessageEndpointAnnotationPostProcessor implements BeanPostProcessor
MessageHandlerCreator handlerCreator = handlerCreators.get(annotation.annotationType());
if (handlerCreator == null) {
if (logger.isWarnEnabled()) {
- logger.warn("No handler creator has been registered for handler annotation '" +
- annotation.annotationType() + "'");
+ logger.warn("No handler creator has been registered for handler annotation '"
+ + annotation.annotationType() + "'");
}
}
else {
diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/spring-integration-core-1.0.xsd b/spring-integration-core/src/main/java/org/springframework/integration/config/spring-integration-core-1.0.xsd
index 371d1e3c8d..8e55b36a0e 100644
--- a/spring-integration-core/src/main/java/org/springframework/integration/config/spring-integration-core-1.0.xsd
+++ b/spring-integration-core/src/main/java/org/springframework/integration/config/spring-integration-core-1.0.xsd
@@ -234,9 +234,13 @@
+
+
+
+
@@ -248,5 +252,17 @@
+
+
+
+
+
+ Defines a completion strategy.
+
+
+
+
+
+
\ No newline at end of file
diff --git a/spring-integration-core/src/main/java/org/springframework/integration/handler/MessageHandlerChain.java b/spring-integration-core/src/main/java/org/springframework/integration/handler/MessageHandlerChain.java
index e07020d0af..428473c4bc 100644
--- a/spring-integration-core/src/main/java/org/springframework/integration/handler/MessageHandlerChain.java
+++ b/spring-integration-core/src/main/java/org/springframework/integration/handler/MessageHandlerChain.java
@@ -16,6 +16,7 @@
package org.springframework.integration.handler;
+import java.util.Collections;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
@@ -32,7 +33,6 @@ public class MessageHandlerChain implements MessageHandler {
private final List handlers = new CopyOnWriteArrayList();
-
/**
* Add a handler to the end of the chain.
*/
@@ -52,6 +52,13 @@ public class MessageHandlerChain implements MessageHandler {
this.handlers.addAll(handlers);
}
+ /**
+ * Get an immutable list of handlers
+ */
+ public List getHandlers() {
+ return Collections.unmodifiableList(handlers);
+ }
+
public final Message> handle(Message> message) {
for (MessageHandler next : handlers) {
message = next.handle(message);
diff --git a/spring-integration-core/src/main/java/org/springframework/integration/router/Aggregator.java b/spring-integration-core/src/main/java/org/springframework/integration/router/Aggregator.java
index a161f64ef6..e748e8dc03 100644
--- a/spring-integration-core/src/main/java/org/springframework/integration/router/Aggregator.java
+++ b/spring-integration-core/src/main/java/org/springframework/integration/router/Aggregator.java
@@ -16,7 +16,7 @@
package org.springframework.integration.router;
-import java.util.Collection;
+import java.util.List;
import org.springframework.integration.message.Message;
@@ -28,6 +28,6 @@ import org.springframework.integration.message.Message;
*/
public interface Aggregator {
- Message> aggregate(Collection> messages);
+ Message> aggregate(List> messages);
}
diff --git a/spring-integration-core/src/main/java/org/springframework/integration/router/AggregatorAdapter.java b/spring-integration-core/src/main/java/org/springframework/integration/router/AggregatorAdapter.java
index 35737f2bfa..de89f84aa1 100644
--- a/spring-integration-core/src/main/java/org/springframework/integration/router/AggregatorAdapter.java
+++ b/spring-integration-core/src/main/java/org/springframework/integration/router/AggregatorAdapter.java
@@ -38,45 +38,18 @@ import org.springframework.util.ReflectionUtils;
* @author Marius Bogoevici
* @author Mark Fisher
*/
-public class AggregatorAdapter implements Aggregator {
-
- private final HandlerMethodInvoker