INT-266 - adding namespace and annotation support for CorrelationStrategy

This commit is contained in:
Marius Bogoevici
2009-02-23 16:18:08 +00:00
parent 03b99960e0
commit 0d960df263
17 changed files with 568 additions and 26 deletions

View File

@@ -0,0 +1,52 @@
/*
* Copyright 2002-2009 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.aggregator;
import java.lang.reflect.Method;
import org.springframework.integration.core.Message;
import org.springframework.integration.handler.MessageMappingMethodInvoker;
import org.springframework.util.Assert;
/**
* {@link CorrelationStrategy} implementation that works as an adapter to another bean.
*
* @author: Marius Bogoevici
*/
public class CorrelationStrategyAdapter implements CorrelationStrategy {
private final MessageMappingMethodInvoker invoker;
public CorrelationStrategyAdapter(Object object, String methodName) {
this.invoker = new MessageMappingMethodInvoker(object, methodName, true);
}
public CorrelationStrategyAdapter(Object object, Method method) {
Assert.notNull(object, "'object' must not be null");
Assert.notNull(method, "'method' must not be null");
Assert.isTrue(method.getParameterTypes().length == 1, "Method must accept exactly one parameter");
Assert.isTrue(!Void.TYPE.equals(method.getReturnType()), "Method return type must not be void");
this.invoker = new MessageMappingMethodInvoker(object, method);
}
public Object getCorrelationKey(Message<?> message) {
return invoker.invokeMethod(message);
}
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright 2002-2009 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 given method is capable of determining the correlation key
* of a message sent as parameter.
*
* @author: Marius Bogoevici
*/
@Retention (RetentionPolicy.RUNTIME)
@Target (ElementType.METHOD)
@Documented
public @interface CorrelationStrategy {
}

View File

@@ -24,8 +24,10 @@ import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.integration.aggregator.AbstractMessageAggregator;
import org.springframework.integration.aggregator.CompletionStrategyAdapter;
import org.springframework.integration.aggregator.MethodInvokingAggregator;
import org.springframework.integration.aggregator.CorrelationStrategyAdapter;
import org.springframework.integration.annotation.Aggregator;
import org.springframework.integration.annotation.CompletionStrategy;
import org.springframework.integration.annotation.CorrelationStrategy;
import org.springframework.integration.core.MessageChannel;
import org.springframework.integration.message.MessageHandler;
import org.springframework.util.Assert;
@@ -48,6 +50,7 @@ public class AggregatorAnnotationPostProcessor extends AbstractMethodAnnotationP
protected MessageHandler createHandler(Object bean, Method method, Aggregator annotation) {
MethodInvokingAggregator aggregator = new MethodInvokingAggregator(bean, method);
this.configureCompletionStrategy(bean, aggregator);
this.configureCorrelationStrategy(bean, aggregator);
String discardChannelName = annotation.discardChannel();
if (StringUtils.hasText(discardChannelName)) {
MessageChannel discardChannel = this.channelResolver.resolveChannelName(discardChannelName);
@@ -79,4 +82,15 @@ public class AggregatorAnnotationPostProcessor extends AbstractMethodAnnotationP
});
}
private void configureCorrelationStrategy(final Object bean, final AbstractMessageAggregator aggregator) {
ReflectionUtils.doWithMethods(bean.getClass(), new ReflectionUtils.MethodCallback() {
public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
Annotation annotation = AnnotationUtils.getAnnotation(method, CorrelationStrategy.class);
if (annotation != null) {
aggregator.setCorrelationStrategy(new CorrelationStrategyAdapter(bean, method));
}
}
});
}
}

View File

@@ -16,13 +16,13 @@
package org.springframework.integration.config.xml;
import org.w3c.dom.Element;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;
/**
* Parser for the <em>aggregator</em> element of the integration namespace.
* Registers the annotation-driven post-processors.
@@ -36,6 +36,10 @@ public class AggregatorParser extends AbstractConsumerEndpointParser {
private static final String COMPLETION_STRATEGY_METHOD_ATTRIBUTE = "completion-strategy-method";
private static final String CORRELATION_STRATEGY_REF_ATTRIBUTE = "correlation-strategy";
private static final String CORRELATION_STRATEGY_METHOD_ATTRIBUTE = "correlation-strategy-method";
private static final String DISCARD_CHANNEL_ATTRIBUTE = "discard-channel";
private static final String SEND_TIMEOUT_ATTRIBUTE = "send-timeout";
@@ -50,6 +54,8 @@ public class AggregatorParser extends AbstractConsumerEndpointParser {
private static final String COMPLETION_STRATEGY_PROPERTY = "completionStrategy";
private static final String CORRELATION_STRATEGY_PROPERTY = "correlationStrategy";
@Override
protected BeanDefinitionBuilder parseHandler(Element element, ParserContext parserContext) {
@@ -71,24 +77,31 @@ public class AggregatorParser extends AbstractConsumerEndpointParser {
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, TRACKED_CORRELATION_ID_CAPACITY_ATTRIBUTE);
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "auto-startup");
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, TIMEOUT_ATTRIBUTE);
final String completionStrategyRef = element.getAttribute(COMPLETION_STRATEGY_REF_ATTRIBUTE);
final String completionStrategyMethod = element.getAttribute(COMPLETION_STRATEGY_METHOD_ATTRIBUTE);
if (StringUtils.hasText(completionStrategyRef)) {
if (StringUtils.hasText(completionStrategyMethod)) {
String adapterBeanName = this.createCompletionStrategyAdapter(
completionStrategyRef, completionStrategyMethod, parserContext);
builder.addPropertyReference(COMPLETION_STRATEGY_PROPERTY, adapterBeanName);
}
else {
builder.addPropertyReference(COMPLETION_STRATEGY_PROPERTY, completionStrategyRef);
}
}
this.injectPropertyWithBean(COMPLETION_STRATEGY_REF_ATTRIBUTE, COMPLETION_STRATEGY_METHOD_ATTRIBUTE, COMPLETION_STRATEGY_PROPERTY,
"CompletionStrategyAdapter", element, builder, parserContext);
this.injectPropertyWithBean(CORRELATION_STRATEGY_REF_ATTRIBUTE, CORRELATION_STRATEGY_METHOD_ATTRIBUTE, CORRELATION_STRATEGY_PROPERTY,
"CorrelationStrategyAdapter", element, builder, parserContext);
return builder;
}
private String createCompletionStrategyAdapter(String ref, String method, ParserContext parserContext) {
private void injectPropertyWithBean(String beanRefAttribute, String methodRefAttribute, String beanProperty, String adapterClass,
Element element, BeanDefinitionBuilder builder, ParserContext parserContext) {
final String beanRef = element.getAttribute(beanRefAttribute);
final String beanMethod = element.getAttribute(methodRefAttribute);
if (StringUtils.hasText(beanRef)) {
if (StringUtils.hasText(beanMethod)) {
String adapterBeanName = this.createAdapter(beanRef, beanMethod, adapterClass, parserContext);
builder.addPropertyReference(beanProperty, adapterBeanName);
}
else {
builder.addPropertyReference(beanProperty, beanRef);
}
}
}
private String createAdapter(String ref, String method, String unqualifiedClassName, ParserContext parserContext) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(
IntegrationNamespaceUtils.BASE_PACKAGE + ".aggregator.CompletionStrategyAdapter");
IntegrationNamespaceUtils.BASE_PACKAGE + ".aggregator." + unqualifiedClassName);
builder.addConstructorArgReference(ref);
builder.addConstructorArgValue(method);
return BeanDefinitionReaderUtils.registerWithGeneratedName(builder.getBeanDefinition(), parserContext.getRegistry());

View File

@@ -823,6 +823,17 @@
</xsd:attribute>
<xsd:attribute name="completion-strategy-method"
type="xsd:string" />
<xsd:attribute name="correlation-strategy" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="java.lang.Object" />
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="correlation-strategy-method"
type="xsd:string" />
<xsd:attribute name="discard-channel" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>

View File

@@ -26,9 +26,6 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.integration.core.Message;
import org.springframework.integration.message.MessageHandlingException;
@@ -40,6 +37,9 @@ import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* A base or helper class for any Messaging component that acts as an adapter
* by invoking a "plain" (not Message-aware) method on a given target object.
@@ -47,6 +47,7 @@ import org.springframework.util.StringUtils;
* 'methodName', or an Annotation type must be provided.
*
* @author Mark Fisher
* @author Marius Bogoevici
*/
public class MessageMappingMethodInvoker {
@@ -78,13 +79,17 @@ public class MessageMappingMethodInvoker {
this.methodResolver = this.createResolverForAnnotation(annotationType);
}
public MessageMappingMethodInvoker(Object object, String methodName) {
public MessageMappingMethodInvoker(Object object, String methodName) {
this(object, methodName, false);
}
public MessageMappingMethodInvoker(Object object, String methodName, boolean requiresReturnValue) {
Assert.notNull(object, "object must not be null");
Assert.notNull(methodName, "methodName must not be null");
this.object = object;
this.methodResolver = this.createResolverForMethodName(methodName);
this.methodResolver = this.createResolverForMethodName(methodName, requiresReturnValue);
}
public Object invokeMethod(Message<?> message) {
Assert.notNull(message, "message must not be null");
@@ -164,11 +169,12 @@ public class MessageMappingMethodInvoker {
return mapper.fromMessage(message);
}
private HandlerMethodResolver createResolverForMethodName(String methodName) {
private HandlerMethodResolver createResolverForMethodName(String methodName, boolean requiresReturnValue) {
List<Method> methodsWithName = new ArrayList<Method>();
Method[] defaultCandidateMethods = HandlerMethodUtils.getCandidateHandlerMethods(this.object);
for (Method method : defaultCandidateMethods) {
if (method.getName().equals(methodName)) {
if (method.getName().equals(methodName)
&& (!requiresReturnValue || !Void.TYPE.equals(method.getReturnType()))) {
methodsWithName.add(method);
}
}