Merge pull request #562 from garyrussell/INT-2214,INT-343/INT-2250/INT-2656

* INT-2214b:
  INT-2214, INT-343, INT-2250 MessageHandler Advice
This commit is contained in:
Oleg Zhurakousky
2012-08-06 09:17:36 -04:00
114 changed files with 2661 additions and 296 deletions

View File

@@ -57,6 +57,7 @@ subprojects { subproject ->
springSecurityVersion = '3.1.0.RELEASE'
springSocialTwitterVersion = '1.0.1.RELEASE'
springWsVersion = '2.1.0.RELEASE'
springRetryVersion = '1.0.2.RELEASE'
}
eclipse {
@@ -176,6 +177,7 @@ project('spring-integration-core') {
compile "org.springframework:spring-aop:$springVersion"
compile "org.springframework:spring-context:$springVersion"
compile "org.springframework:spring-tx:$springVersion"
compile "org.springframework.retry:spring-retry:$springRetryVersion"
compile("org.codehaus.jackson:jackson-mapper-asl:$jacksonVersion", optional)
testCompile "org.aspectj:aspectjrt:$aspectjVersion"
testCompile "org.aspectj:aspectjweaver:$aspectjVersion"
@@ -185,6 +187,7 @@ project('spring-integration-core') {
importTemplate += [
'org.springframework.*;version="[3.1.1, 4.0.0)"',
'org.springframework.transaction;version="[3.1.1, 4.0.0)";resolution:=optional',
'org.springframework.retry;version="[1.0.2, 2.0.0)"',
'org.apache.commons.logging;version="[1.1.1, 2.0.0)"',
'org.aopalliance.*;version="[1.0.0, 2.0.0)"',
'org.codehaus.jackson.*;version="[1.0.0, 2.0.0)";resolution:=optional',

View File

@@ -368,6 +368,9 @@
Base type for the 'outbound-channel-adapter' and 'outbound-gateway' elements.
</xsd:documentation>
</xsd:annotation>
<xsd:sequence>
<xsd:element name="request-handler-advice-chain" type="integration:adviceChainType" minOccurs="0" maxOccurs="1" />
</xsd:sequence>
<xsd:attribute name="exchange-name" type="xsd:string">
<xsd:annotation>
<xsd:documentation>

View File

@@ -9,7 +9,11 @@
http://www.springframework.org/schema/rabbit http://www.springframework.org/schema/rabbit/spring-rabbit.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<amqp:outbound-channel-adapter id="rabbitOutbound" exchange-name="outboundchanneladapter.test.1"/>
<amqp:outbound-channel-adapter id="rabbitOutbound" exchange-name="outboundchanneladapter.test.1">
<amqp:request-handler-advice-chain>
<bean class="org.springframework.integration.amqp.config.AmqpOutboundChannelAdapterParserTests$FooAdvice" />
</amqp:request-handler-advice-chain>
</amqp:outbound-channel-adapter>
<rabbit:template id="amqpTemplate" connection-factory="connectionFactory"/>

View File

@@ -20,12 +20,13 @@ import static junit.framework.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.lang.reflect.Field;
import java.util.List;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.junit.Test;
@@ -48,10 +49,12 @@ import org.springframework.integration.amqp.AmqpHeaders;
import org.springframework.integration.amqp.outbound.AmqpOutboundEndpoint;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.integration.channel.NullChannel;
import org.springframework.integration.context.NamedComponent;
import org.springframework.integration.core.MessageHandler;
import org.springframework.integration.core.PollableChannel;
import org.springframework.integration.endpoint.EventDrivenConsumer;
import org.springframework.integration.handler.MessageHandlerChain;
import org.springframework.integration.handler.advice.AbstractRequestHandlerAdvice;
import org.springframework.integration.message.GenericMessage;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.integration.test.util.TestUtils;
import org.springframework.test.context.ContextConfiguration;
@@ -72,6 +75,8 @@ import com.rabbitmq.client.Channel;
@RunWith(SpringJUnit4ClassRunner.class)
public class AmqpOutboundChannelAdapterParserTests {
private static volatile int adviceCalled;
@Autowired
private ApplicationContext context;
@@ -82,16 +87,18 @@ public class AmqpOutboundChannelAdapterParserTests {
assertEquals(DirectChannel.class, channel.getClass());
assertEquals(EventDrivenConsumer.class, adapter.getClass());
MessageHandler handler = TestUtils.getPropertyValue(adapter, "handler", MessageHandler.class);
assertEquals(AmqpOutboundEndpoint.class, handler.getClass());
assertEquals("amqp:outbound-channel-adapter", ((AmqpOutboundEndpoint) handler).getComponentType());
assertTrue(handler instanceof NamedComponent);
assertEquals("amqp:outbound-channel-adapter", ((NamedComponent) handler).getComponentType());
handler.handleMessage(new GenericMessage<String>("foo"));
assertEquals(1, adviceCalled);
}
@Test
public void withHeaderMapperCustomHeaders() {
Object eventDrivenConsumer = context.getBean("withHeaderMapperCustomHeaders");
AmqpOutboundEndpoint endpoint = TestUtils.getPropertyValue(eventDrivenConsumer, "handler", AmqpOutboundEndpoint.class);
Field amqpTemplateField = ReflectionUtils.findField(AmqpOutboundEndpoint.class, "amqpTemplate");
amqpTemplateField.setAccessible(true);
RabbitTemplate amqpTemplate = TestUtils.getPropertyValue(endpoint, "amqpTemplate", RabbitTemplate.class);
@@ -110,8 +117,8 @@ public class AmqpOutboundChannelAdapterParserTests {
.when(amqpTemplate).send(Mockito.any(String.class), Mockito.any(String.class),
Mockito.any(org.springframework.amqp.core.Message.class), Mockito.any(CorrelationData.class));
ReflectionUtils.setField(amqpTemplateField, endpoint, amqpTemplate);
MessageChannel requestChannel = context.getBean("requestChannel", MessageChannel.class);
Message<?> message = MessageBuilder.withPayload("hello").setHeader("foo", "foo").setHeader("bar", "bar").setHeader("foobar", "foobar").build();
requestChannel.send(message);
@@ -214,4 +221,14 @@ public class AmqpOutboundChannelAdapterParserTests {
assertEquals("bar", returned.getHeaders().get(AmqpHeaders.RETURN_ROUTING_KEY));
assertEquals("hello", returned.getPayload());
}
public static class FooAdvice extends AbstractRequestHandlerAdvice {
@Override
protected Object doInvoke(ExecutionCallback callback, Object target, Message<?> message) throws Exception {
adviceCalled++;
return null;
}
}
}

View File

@@ -55,7 +55,11 @@
amqp-template="amqpTemplate"
order="5"
mapped-request-headers=""
mapped-reply-headers=""/>
mapped-reply-headers="">
<amqp:request-handler-advice-chain>
<bean class="org.springframework.integration.amqp.config.AmqpOutboundGatewayParserTests$FooAdvice" />
</amqp:request-handler-advice-chain>
</amqp:outbound-gateway>
<int:channel id="returnChannel">
<int:queue/>

View File

@@ -15,6 +15,12 @@
*/
package org.springframework.integration.amqp.config;
import static junit.framework.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import java.lang.reflect.Field;
import java.util.List;
@@ -31,17 +37,11 @@ import org.springframework.integration.MessageChannel;
import org.springframework.integration.amqp.AmqpHeaders;
import org.springframework.integration.amqp.outbound.AmqpOutboundEndpoint;
import org.springframework.integration.channel.QueueChannel;
import org.springframework.integration.handler.advice.AbstractRequestHandlerAdvice;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.integration.test.util.TestUtils;
import org.springframework.util.ReflectionUtils;
import static junit.framework.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
/**
* @author Oleg Zhurakousky
* @author Gary Russell
@@ -53,6 +53,8 @@ import static org.junit.Assert.assertTrue;
*/
public class AmqpOutboundGatewayParserTests {
private static volatile int adviceCalled;
@Test
public void testGatewayConfig(){
ApplicationContext context = new ClassPathXmlApplicationContext("AmqpOutboundGatewayParserTests-context.xml", this.getClass());
@@ -209,6 +211,7 @@ public class AmqpOutboundGatewayParserTests {
assertNull(replyMessage.getHeaders().get(AmqpHeaders.DELIVERY_MODE));
assertNull(replyMessage.getHeaders().get(AmqpHeaders.CONTENT_TYPE));
assertNull(replyMessage.getHeaders().get(AmqpHeaders.APP_ID));
assertEquals(1, adviceCalled);
}
@Test //INT-1029
@@ -261,4 +264,13 @@ public class AmqpOutboundGatewayParserTests {
}
public static class FooAdvice extends AbstractRequestHandlerAdvice {
@Override
protected Object doInvoke(ExecutionCallback callback, Object target, Message<?> message) throws Exception {
adviceCalled++;
return callback.execute();
}
}
}

View File

@@ -91,6 +91,8 @@ public final class MessageHeaders implements Map<String, Object>, Serializable {
public static final String DISPOSITION_RESULT = "dispositionResult";
public static final String POSTPROCESS_RESULT = "postProcessResult";
private final Map<String, Object> headers;
@@ -164,12 +166,12 @@ public final class MessageHeaders implements Map<String, Object>, Serializable {
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
public boolean equals(Object object) {
if (this == object) {
return true;
}
if (obj != null && obj instanceof MessageHeaders) {
MessageHeaders other = (MessageHeaders) obj;
if (object != null && object instanceof MessageHeaders) {
MessageHeaders other = (MessageHeaders) object;
return this.headers.equals(other.headers);
}
return false;

View File

@@ -1,11 +1,11 @@
/*
* 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. 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.
@@ -13,6 +13,9 @@
package org.springframework.integration.config;
import java.util.List;
import org.aopalliance.aop.Advice;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
@@ -23,11 +26,14 @@ import org.springframework.integration.MessageChannel;
import org.springframework.integration.context.Orderable;
import org.springframework.integration.core.MessageHandler;
import org.springframework.integration.core.MessageProducer;
import org.springframework.integration.handler.AbstractReplyProducingMessageHandler;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
/**
* @author Dave Syer
* @author Oleg Zhurakousky
* @author Gary Russell
*/
public abstract class AbstractSimpleMessageHandlerFactoryBean<H extends MessageHandler> implements FactoryBean<MessageHandler>, BeanFactoryAware {
@@ -43,6 +49,8 @@ public abstract class AbstractSimpleMessageHandlerFactoryBean<H extends MessageH
private final Object initializationMonitor = new Object();
private volatile List<Advice> adviceChain;
public AbstractSimpleMessageHandlerFactoryBean() {
super();
@@ -64,6 +72,10 @@ public abstract class AbstractSimpleMessageHandlerFactoryBean<H extends MessageH
return this.beanFactory;
}
public void setAdviceChain(List<Advice> adviceChain) {
this.adviceChain = adviceChain;
}
public H getObject() throws Exception {
if (this.handler == null) {
this.handler = this.createHandlerInternal();
@@ -71,9 +83,6 @@ public abstract class AbstractSimpleMessageHandlerFactoryBean<H extends MessageH
if (this.handler instanceof MessageProducer && this.outputChannel != null) {
((MessageProducer) this.handler).setOutputChannel(this.outputChannel);
}
if (this.handler instanceof BeanFactoryAware) {
((BeanFactoryAware) this.handler).setBeanFactory(beanFactory);
}
if (this.handler instanceof Orderable && this.order != null) {
((Orderable) this.handler).setOrder(this.order.intValue());
}
@@ -91,6 +100,10 @@ public abstract class AbstractSimpleMessageHandlerFactoryBean<H extends MessageH
if (handler instanceof BeanFactoryAware) {
((BeanFactoryAware) handler).setBeanFactory(getBeanFactory());
}
if (!CollectionUtils.isEmpty(this.adviceChain) &&
this.handler instanceof AbstractReplyProducingMessageHandler) {
((AbstractReplyProducingMessageHandler) this.handler).setAdviceChain(this.adviceChain);
}
this.initialized = true;
}
if (handler instanceof InitializingBean) {

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.
@@ -15,10 +15,15 @@
*/
package org.springframework.integration.config;
import java.util.List;
import org.aopalliance.aop.Advice;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.aop.framework.Advised;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.support.AopUtils;
import org.springframework.aop.support.NameMatchMethodPointcutAdvisor;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
@@ -36,8 +41,10 @@ import org.springframework.integration.core.SubscribableChannel;
import org.springframework.integration.endpoint.AbstractEndpoint;
import org.springframework.integration.endpoint.EventDrivenConsumer;
import org.springframework.integration.endpoint.PollingConsumer;
import org.springframework.integration.handler.AbstractReplyProducingMessageHandler;
import org.springframework.integration.scheduling.PollerMetadata;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
/**
@@ -56,13 +63,13 @@ public class ConsumerEndpointFactoryBean
private volatile String inputChannelName;
private volatile PollerMetadata pollerMetadata;
private volatile boolean autoStartup = true;
private volatile MessageChannel inputChannel;
private volatile ConfigurableBeanFactory beanFactory;
private volatile ClassLoader beanClassLoader;
private volatile AbstractEndpoint endpoint;
@@ -75,6 +82,8 @@ public class ConsumerEndpointFactoryBean
private final Log logger = LogFactory.getLog(this.getClass());
private volatile List<Advice> adviceChain;
public void setHandler(MessageHandler handler) {
Assert.notNull(handler, "handler must not be null");
synchronized (this.handlerMonitor) {
@@ -94,7 +103,7 @@ public class ConsumerEndpointFactoryBean
public void setPollerMetadata(PollerMetadata pollerMetadata) {
this.pollerMetadata = pollerMetadata;
}
public void setBeanClassLoader(ClassLoader classLoader) {
this.beanClassLoader = classLoader;
}
@@ -112,6 +121,11 @@ public class ConsumerEndpointFactoryBean
this.beanFactory = (ConfigurableBeanFactory) beanFactory;
}
public void setAdviceChain(List<Advice> adviceChain) {
Assert.notNull(adviceChain, "adviceChain must not be null");
this.adviceChain = adviceChain;
}
public void afterPropertiesSet() throws Exception {
try {
if (!this.beanName.startsWith("org.springframework")) {
@@ -132,6 +146,32 @@ public class ConsumerEndpointFactoryBean
+ this.handler + " for " + this.beanName + " :" + e.getMessage());
}
}
if (!CollectionUtils.isEmpty(this.adviceChain)) {
/*
* ARPMHs advise the handleRequesMessage method internally and already have the advice chain injected.
* So we only advise handlers that are not reply-producing. If the handler is already advised,
* add the configured advices to its chain, otherwise create a proxy.
*/
if (!(this.handler instanceof AbstractReplyProducingMessageHandler)) {
if (AopUtils.isAopProxy(this.handler) && this.handler instanceof Advised) {
Class<?> targetClass = AopUtils.getTargetClass(this.handler);
for (Advice advice : this.adviceChain) {
NameMatchMethodPointcutAdvisor handlerAdvice = new NameMatchMethodPointcutAdvisor(advice);
handlerAdvice.addMethodName("handleMessage");
if (AopUtils.canApply(handlerAdvice.getPointcut(), targetClass)) {
((Advised) this.handler).addAdvice(advice);
}
}
}
else {
ProxyFactory proxyFactory = new ProxyFactory(this.handler);
for (Advice advice : this.adviceChain) {
proxyFactory.addAdvice(advice);
}
this.handler = (MessageHandler) proxyFactory.getProxy(this.beanClassLoader);
}
}
}
this.initializeEndpoint();
}
@@ -184,9 +224,9 @@ public class ConsumerEndpointFactoryBean
pollingConsumer.setTrigger(this.pollerMetadata.getTrigger());
pollingConsumer.setAdviceChain(this.pollerMetadata.getAdviceChain());
pollingConsumer.setMaxMessagesPerPoll(this.pollerMetadata.getMaxMessagesPerPoll());
pollingConsumer.setErrorHandler(this.pollerMetadata.getErrorHandler());
pollingConsumer.setReceiveTimeout(this.pollerMetadata.getReceiveTimeout());
pollingConsumer.setBeanClassLoader(beanClassLoader);
pollingConsumer.setBeanFactory(beanFactory);

View File

@@ -19,8 +19,7 @@ package org.springframework.integration.config.xml;
import java.util.Collection;
import java.util.List;
import org.w3c.dom.Element;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConstructorArgumentValues;
import org.springframework.beans.factory.config.ConstructorArgumentValues.ValueHolder;
@@ -32,10 +31,10 @@ import org.springframework.beans.factory.support.ManagedSet;
import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.integration.config.ConsumerEndpointFactoryBean;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils;
import org.w3c.dom.Element;
/**
* Base class parser for elements that create Message Endpoints.
@@ -92,6 +91,11 @@ public abstract class AbstractConsumerEndpointParser extends AbstractBeanDefinit
return handlerBeanDefinition;
}
Element adviceChainElement = DomUtils.getChildElementByTagName(element,
IntegrationNamespaceUtils.REQUEST_HANDLER_ADVICE_CHAIN);
IntegrationNamespaceUtils.configureAndSetAdviceChainIfPresent(adviceChainElement, null,
handlerBuilder, parserContext);
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(ConsumerEndpointFactoryBean.class);
String handlerBeanName = BeanDefinitionReaderUtils.generateBeanName(handlerBeanDefinition, parserContext.getRegistry());
@@ -99,6 +103,7 @@ public abstract class AbstractConsumerEndpointParser extends AbstractBeanDefinit
parserContext.registerBeanComponent(new BeanComponentDefinition(handlerBeanDefinition, handlerBeanName, handlerAlias));
builder.addPropertyReference("handler", handlerBeanName);
String inputChannelName = element.getAttribute(inputChannelAttributeName);
if (!parserContext.getRegistry().containsBeanDefinition(inputChannelName)){
@@ -136,4 +141,5 @@ public abstract class AbstractConsumerEndpointParser extends AbstractBeanDefinit
parserContext.registerBeanComponent(new BeanComponentDefinition(beanDefinition, beanName));
return null;
}
}

View File

@@ -16,16 +16,18 @@
package org.springframework.integration.config.xml;
import org.w3c.dom.Element;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
import org.springframework.beans.factory.support.ManagedList;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.integration.config.ConsumerEndpointFactoryBean;
import org.springframework.integration.handler.AbstractReplyProducingMessageHandler;
import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils;
import org.w3c.dom.Element;
/**
* Base class for outbound Channel Adapter parsers.
@@ -34,7 +36,7 @@ import org.springframework.util.xml.DomUtils;
* an {@link org.springframework.integration.endpoint.AbstractEndpoint} depending on the channel type.
* If this component is defined as nested element (e.g., inside of the chain) it will produce
* a {@link org.springframework.integration.core.MessageHandler}.
*
*
* @author Mark Fisher
* @author Gary Russell
* @author Artem Bilan
@@ -48,7 +50,8 @@ public abstract class AbstractOutboundChannelAdapterParser extends AbstractChann
}
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(ConsumerEndpointFactoryBean.class);
Element pollerElement = DomUtils.getChildElementByTagName(element, "poller");
builder.addPropertyReference("handler", this.parseAndRegisterConsumer(element, parserContext));
BeanComponentDefinition handlerBeanComponentDefinition = this.doParseAndRegisterConsumer(element, parserContext);
builder.addPropertyReference("handler", handlerBeanComponentDefinition.getBeanName());
if (pollerElement != null) {
if (!StringUtils.hasText(channelName)) {
parserContext.getReaderContext().error(
@@ -58,6 +61,40 @@ public abstract class AbstractOutboundChannelAdapterParser extends AbstractChann
}
builder.addPropertyValue("inputChannelName", channelName);
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "auto-startup");
Element adviceChainElement = DomUtils.getChildElementByTagName(element,
IntegrationNamespaceUtils.REQUEST_HANDLER_ADVICE_CHAIN);
@SuppressWarnings("rawtypes")
ManagedList adviceChain = IntegrationNamespaceUtils.configureAdviceChain(adviceChainElement, null,
builder, parserContext);
if (adviceChain != null) {
BeanDefinition handlerBeanDefinition = handlerBeanComponentDefinition.getBeanDefinition();
/*
* For ARPMH, the advice chain is injected so just the handleRequestMessage method is advised.
* Sometime ARPMHs do double duty as a gateway and a channel adapter. The parser subclass
* can indicate this by overriding isUsingReplyProducer(), or we can try to determine it from
* the bean class.
*/
boolean isReplyProducer = this.isUsingReplyProducer();
if (!isReplyProducer) {
Class<?> beanClass = null;
if (handlerBeanDefinition instanceof AbstractBeanDefinition) {
AbstractBeanDefinition abstractBeanDefinition = (AbstractBeanDefinition) handlerBeanDefinition;
if (abstractBeanDefinition.hasBeanClass()) {
beanClass = abstractBeanDefinition.getBeanClass();
}
}
isReplyProducer = beanClass != null && AbstractReplyProducingMessageHandler.class.isAssignableFrom(beanClass);
}
if (isReplyProducer) {
handlerBeanDefinition.getPropertyValues().add("adviceChain", adviceChain);
}
else {
builder.addPropertyValue("adviceChain", adviceChain);
}
}
return builder.getBeanDefinition();
}
@@ -65,8 +102,19 @@ public abstract class AbstractOutboundChannelAdapterParser extends AbstractChann
* Override this method to control the registration process and return the bean name.
* If parsing a bean definition whose name can be auto-generated, consider using
* {@link #parseConsumer(Element, ParserContext)} instead.
* @deprecated Use {@link #doParseAndRegisterConsumer(Element, ParserContext)}
*/
@Deprecated
protected String parseAndRegisterConsumer(Element element, ParserContext parserContext) {
return doParseAndRegisterConsumer(element, parserContext).getBeanName();
}
/**
* Override this method to control the registration process and return the bean name.
* If parsing a bean definition whose name can be auto-generated, consider using
* {@link #parseConsumer(Element, ParserContext)} instead.
*/
protected BeanComponentDefinition doParseAndRegisterConsumer(Element element, ParserContext parserContext) {
AbstractBeanDefinition definition = this.parseConsumer(element, parserContext);
if (definition == null) {
parserContext.getReaderContext().error("Consumer parsing must return an AbstractBeanDefinition.", element);
@@ -77,8 +125,9 @@ public abstract class AbstractOutboundChannelAdapterParser extends AbstractChann
}
String beanName = BeanDefinitionReaderUtils.generateBeanName(definition, parserContext.getRegistry());
String[] handlerAlias = IntegrationNamespaceUtils.generateAlias(element);
parserContext.registerBeanComponent(new BeanComponentDefinition(definition, beanName, handlerAlias));
return beanName;
BeanComponentDefinition beanComponentDefinition = new BeanComponentDefinition(definition, beanName, handlerAlias);
parserContext.registerBeanComponent(beanComponentDefinition);
return beanComponentDefinition;
}
/**
@@ -87,4 +136,13 @@ public abstract class AbstractOutboundChannelAdapterParser extends AbstractChann
*/
protected abstract AbstractBeanDefinition parseConsumer(Element element, ParserContext parserContext);
/**
* Override this to signal that this channel adapter is actually using a AbstractReplyProducingMessageHandler
* while it is not possible for this parser to determine that because, say, a FactoryBean is being used.
* @return false, unless overridden.
*/
protected boolean isUsingReplyProducer() {
return false;
}
}

View File

@@ -19,9 +19,11 @@ import java.util.List;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.config.TypedStringValue;
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.ManagedList;
import org.springframework.beans.factory.xml.BeanDefinitionParserDelegate;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.core.Conventions;
@@ -33,6 +35,8 @@ import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/**
* Shared utility methods for integration namespace parsers.
@@ -54,6 +58,7 @@ public abstract class IntegrationNamespaceUtils {
static final String ORDER = "order";
static final String EXPRESSION_ATTRIBUTE = "expression";
public static final String HANDLER_ALIAS_SUFFIX = ".handler";
public static final String REQUEST_HANDLER_ADVICE_CHAIN = "request-handler-advice-chain";
/**
* Property name on ChannelInitializer used to configure the default max subscribers for
@@ -308,4 +313,56 @@ public abstract class IntegrationNamespaceUtils {
}
return handlerAlias;
}
@SuppressWarnings({ "rawtypes" })
public static void configureAndSetAdviceChainIfPresent(Element adviceChainElement, Element txElement,
BeanDefinitionBuilder parentBuilder, ParserContext parserContext) {
ManagedList adviceChain = configureAdviceChain(adviceChainElement, txElement, parentBuilder, parserContext);
if (adviceChain != null) {
parentBuilder.addPropertyValue("adviceChain", adviceChain);
}
}
@SuppressWarnings({ "rawtypes", "unchecked" })
public static ManagedList configureAdviceChain(Element adviceChainElement, Element txElement,
BeanDefinitionBuilder parentBuilder, ParserContext parserContext) {
ManagedList adviceChain = null;
// Schema validation ensures txElement and adviceChainElement are mutually exclusive
if (txElement != null) {
adviceChain = new ManagedList();
adviceChain.add(IntegrationNamespaceUtils.configureTransactionAttributes(txElement));
}
if (adviceChainElement != null) {
adviceChain = new ManagedList();
NodeList childNodes = adviceChainElement.getChildNodes();
for (int i = 0; i < childNodes.getLength(); i++) {
Node child = childNodes.item(i);
if (child.getNodeType() == Node.ELEMENT_NODE) {
Element childElement = (Element) child;
String localName = child.getLocalName();
if ("bean".equals(localName)) {
BeanDefinitionHolder holder = parserContext.getDelegate().parseBeanDefinitionElement(
childElement, parentBuilder.getBeanDefinition());
parserContext.registerBeanComponent(new BeanComponentDefinition(holder));
adviceChain.add(new RuntimeBeanReference(holder.getBeanName()));
}
else if ("ref".equals(localName)) {
String ref = childElement.getAttribute("bean");
adviceChain.add(new RuntimeBeanReference(ref));
}
else {
BeanDefinition customBeanDefinition = parserContext.getDelegate().parseCustomElement(
childElement, parentBuilder.getBeanDefinition());
if (customBeanDefinition == null) {
parserContext.getReaderContext().error(
"failed to parse custom element '" + localName + "'", childElement);
}
adviceChain.add(customBeanDefinition);
}
}
}
}
return adviceChain;
}
}

View File

@@ -20,14 +20,9 @@ import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
import org.springframework.beans.factory.support.ManagedList;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
@@ -40,8 +35,6 @@ import org.springframework.scheduling.support.PeriodicTrigger;
import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/**
* Parser for the &lt;poller&gt; element.
@@ -94,7 +87,8 @@ public class PollerParser extends AbstractBeanDefinitionParser {
Element txElement = DomUtils.getChildElementByTagName(element, "transactional");
Element adviceChainElement = DomUtils.getChildElementByTagName(element, "advice-chain");
configureAdviceChain(adviceChainElement, txElement, metadataBuilder, parserContext);
IntegrationNamespaceUtils.configureAndSetAdviceChainIfPresent(adviceChainElement, txElement,
metadataBuilder, parserContext);
Element pseudoTxElement = DomUtils.getChildElementByTagName(element, "psuedo-transactional");
if (pseudoTxElement != null && txElement != null) {
@@ -174,48 +168,6 @@ public class PollerParser extends AbstractBeanDefinitionParser {
targetBuilder.addPropertyReference("trigger", triggerBeanNames.get(0));
}
/**
* Parses the 'advice-chain' element's sub-elements.
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
private void configureAdviceChain(Element adviceChainElement, Element txElement, BeanDefinitionBuilder targetBuilder, ParserContext parserContext) {
ManagedList adviceChain = new ManagedList();
// Schema validation ensures txElement and adviceChainElement are mutually exclusive
if (txElement != null) {
adviceChain.add(IntegrationNamespaceUtils.configureTransactionAttributes(txElement));
}
if (adviceChainElement != null) {
NodeList childNodes = adviceChainElement.getChildNodes();
for (int i = 0; i < childNodes.getLength(); i++) {
Node child = childNodes.item(i);
if (child.getNodeType() == Node.ELEMENT_NODE) {
Element childElement = (Element) child;
String localName = child.getLocalName();
if ("bean".equals(localName)) {
BeanDefinitionHolder holder = parserContext.getDelegate().parseBeanDefinitionElement(
childElement, targetBuilder.getBeanDefinition());
parserContext.registerBeanComponent(new BeanComponentDefinition(holder));
adviceChain.add(new RuntimeBeanReference(holder.getBeanName()));
}
else if ("ref".equals(localName)) {
String ref = childElement.getAttribute("bean");
adviceChain.add(new RuntimeBeanReference(ref));
}
else {
BeanDefinition customBeanDefinition = parserContext.getDelegate().parseCustomElement(
childElement, targetBuilder.getBeanDefinition());
if (customBeanDefinition == null) {
parserContext.getReaderContext().error(
"failed to parse custom element '" + localName + "'", childElement);
}
adviceChain.add(customBeanDefinition);
}
}
}
}
targetBuilder.addPropertyValue("adviceChain", adviceChain);
}
private void configureTransactionSync(Element element, BeanDefinitionBuilder metadataBuilder,
ParserContext parserContext) {
if (element != null) {

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.
@@ -16,6 +16,11 @@
package org.springframework.integration.handler;
import java.util.List;
import org.aopalliance.aop.Advice;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.integration.Message;
import org.springframework.integration.MessageChannel;
import org.springframework.integration.MessageDeliveryException;
@@ -26,6 +31,8 @@ import org.springframework.integration.support.MessageBuilder;
import org.springframework.integration.support.channel.ChannelResolutionException;
import org.springframework.integration.support.channel.ChannelResolver;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils;
/**
* Base class for MessageHandlers that are capable of producing replies.
@@ -33,8 +40,10 @@ import org.springframework.util.Assert;
* @author Mark Fisher
* @author Iwein Fuld
* @author Oleg Zhurakousky
* @author Gary Russell
*/
public abstract class AbstractReplyProducingMessageHandler extends AbstractMessageHandler implements MessageProducer {
public abstract class AbstractReplyProducingMessageHandler extends AbstractMessageHandler
implements MessageProducer, BeanClassLoaderAware {
private MessageChannel outputChannel;
@@ -42,6 +51,13 @@ public abstract class AbstractReplyProducingMessageHandler extends AbstractMessa
private final MessagingTemplate messagingTemplate;
private volatile RequestHandler advisedRequestHandler;
private volatile List<Advice> adviceChain;
private volatile ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();
public AbstractReplyProducingMessageHandler() {
this.messagingTemplate = new MessagingTemplate();
@@ -82,11 +98,30 @@ public abstract class AbstractReplyProducingMessageHandler extends AbstractMessa
return this.messagingTemplate;
}
public void setAdviceChain(List<Advice> adviceChain) {
Assert.notNull(adviceChain, "adviceChain cannot be null");
this.adviceChain = adviceChain;
}
public void setBeanClassLoader(ClassLoader beanClassLoader) {
this.beanClassLoader = beanClassLoader;
}
@Override
protected void onInit() {
if (this.getBeanFactory() != null) {
this.messagingTemplate.setBeanFactory(getBeanFactory());
}
if (!CollectionUtils.isEmpty(this.adviceChain)) {
ProxyFactory proxyFactory = new ProxyFactory(new AdvisedRequestHandler());
for (Advice advice : this.adviceChain) {
proxyFactory.addAdvice(advice);
}
this.advisedRequestHandler = (RequestHandler) proxyFactory.getProxy(this.beanClassLoader);
}
}
/**
@@ -94,7 +129,13 @@ public abstract class AbstractReplyProducingMessageHandler extends AbstractMessa
*/
@Override
protected final void handleMessageInternal(Message<?> message) {
Object result = this.handleRequestMessage(message);
Object result;
if (this.advisedRequestHandler == null) {
result = this.handleRequestMessage(message);
}
else {
result = this.advisedRequestHandler.handleRequestMessage(message);
}
if (result != null) {
MessageHeaders requestHeaders = message.getHeaders();
this.handleResult(result, requestHeaders);
@@ -149,7 +190,7 @@ public abstract class AbstractReplyProducingMessageHandler extends AbstractMessa
* 'outputChannel' is <code>null</code>. In that case, the header value must not also be
* <code>null</code>, and it must be an instance of either String or {@link MessageChannel}.
* @param replyMessage the reply Message to send
* @param replyChannelHeaderValue the 'replyChannel' header value from the original request
* @param replyChannelHeaderValue the 'replyChannel' header value from the original request
*/
private final void sendReplyMessage(Message<?> replyMessage, final Object replyChannelHeaderValue) {
if (logger.isDebugEnabled()) {
@@ -207,4 +248,26 @@ public abstract class AbstractReplyProducingMessageHandler extends AbstractMessa
*/
protected abstract Object handleRequestMessage(Message<?> requestMessage);
private interface RequestHandler {
Object handleRequestMessage(Message<?> requestMessage);
String toString();
}
private class AdvisedRequestHandler implements RequestHandler {
public Object handleRequestMessage(Message<?> requestMessage) {
return AbstractReplyProducingMessageHandler.this.handleRequestMessage(requestMessage);
}
@Override
public String toString() {
return AbstractReplyProducingMessageHandler.this.toString();
}
}
}

View File

@@ -0,0 +1,31 @@
/*
* 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.handler;
import org.springframework.integration.Message;
import org.springframework.retry.RetryState;
/**
* Strategy interface for generating a {@link RetryState} instance
* based on a message.
* @author Gary Russell
* @since 2.2
*
*/
public interface RetryStateGenerator {
RetryState determineRetryState(Message<?> message);
}

View File

@@ -0,0 +1,87 @@
/*
* 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.handler.advice;
import java.lang.reflect.Method;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.integration.Message;
import org.springframework.integration.core.MessageHandler;
/**
* Base class for {@link MessageHandler} advice classes.
* @author Gary Russell
* @since 2.2
*
*/
public abstract class AbstractRequestHandlerAdvice implements MethodInterceptor {
protected final Log logger = LogFactory.getLog(this.getClass());
public final Object invoke(final MethodInvocation invocation) throws Throwable {
Method method = invocation.getMethod();
Object[] arguments = invocation.getArguments();
boolean isMessageMethod = (method.getName().equals("handleRequestMessage") || method.getName().equals("handleMessage"))
&& (arguments.length == 1 && arguments[0] instanceof Message);
if (!isMessageMethod) {
return invocation.proceed();
}
else {
Message<?> message = (Message<?>) arguments[0];
try {
return doInvoke(new ExecutionCallback(){
public Object execute() throws Exception {
try {
return invocation.proceed();
}
catch (Throwable e) {
throw new ThrowableHolderException(e);
}
}
}, invocation.getThis(), message);
}
catch (Exception e) {
if (e instanceof ThrowableHolderException) {
throw e.getCause();
}
else {
throw e;
}
}
}
}
protected abstract Object doInvoke(ExecutionCallback callback, Object target, Message<?> message) throws Exception;
protected interface ExecutionCallback {
Object execute() throws Exception;
}
@SuppressWarnings("serial")
private class ThrowableHolderException extends RuntimeException {
public ThrowableHolderException(Throwable cause) {
super(cause);
}
}
}

View File

@@ -0,0 +1,166 @@
/*
* 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.handler.advice;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.expression.Expression;
import org.springframework.integration.Message;
import org.springframework.integration.MessageChannel;
import org.springframework.integration.MessageHeaders;
import org.springframework.integration.core.MessageHandler;
import org.springframework.integration.core.MessagingTemplate;
import org.springframework.integration.handler.ExpressionEvaluatingMessageProcessor;
import org.springframework.integration.support.MessageBuilder;
/**
* Used to advise {@link MessageHandler}s.
* Two expressions 'onSuccessExpression' and 'onFailureExpression' are evaluated when
* appropriate. If the evaluation returns a result, a message is sent to the onSuccessChannel
* or onFailureChannel as appropriate; the message is the input message with a header
* {@link MessageHeaders#POSTPROCESS_RESULT} containing the evaluation result.
* The failure expression is NOT evaluated if the success expression throws an exception.
* @author Gary Russell
* @since 2.2
*
*/
public class ExpressionEvaluatingRequestHandlerAdvice extends AbstractRequestHandlerAdvice
implements BeanFactoryAware {
private final ExpressionEvaluatingMessageProcessor<Object> onSuccessMessageProcessor;
private final MessageChannel successChannel;
private final ExpressionEvaluatingMessageProcessor<Object> onFailureMessageProcessor;
private final MessageChannel failureChannel;
private final MessagingTemplate messagingTemplate = new MessagingTemplate();
private volatile boolean trapException = false;
private volatile boolean returnFailureExpressionResult = false;
private volatile BeanFactory beanFactory;
private volatile boolean propagateOnSuccessEvaluationFailures;
/**
* @param onSuccessExpression
* @param successChannel
* @param onFailureExpression
* @param failureChannel
*/
public ExpressionEvaluatingRequestHandlerAdvice(Expression onSuccessExpression, MessageChannel successChannel,
Expression onFailureExpression, MessageChannel failureChannel) {
if (onSuccessExpression != null) {
this.onSuccessMessageProcessor = new ExpressionEvaluatingMessageProcessor<Object>(onSuccessExpression);
this.onSuccessMessageProcessor.setBeanFactory(this.beanFactory);
}
else {
this.onSuccessMessageProcessor = null;
}
this.successChannel = successChannel;
if (onFailureExpression != null) {
this.onFailureMessageProcessor = new ExpressionEvaluatingMessageProcessor<Object>(onFailureExpression);
onFailureMessageProcessor.setBeanFactory(this.beanFactory);
}
else {
this.onFailureMessageProcessor = null;
}
this.failureChannel = failureChannel;
}
/**
* If true, any exception will be caught and null returned.
* Default false.
* @param trapException
*/
public void setTrapException(boolean trapException) {
this.trapException = trapException;
}
/**
* If true, the result of evaluating the onFailureExpression will
* be returned as the result of AbstractReplyProducingMessageHandler.handleRequestMessage(Message).
* @param returnFailureExpressionResult
*/
public void setReturnFailureExpressionResult(boolean returnFailureExpressionResult) {
this.returnFailureExpressionResult = returnFailureExpressionResult;
}
/**
* If true and an onSuccess expression evaluation fails with an exception, the exception will be thrown to the
* caller. If false, the exception is caught. Default false. Ignored for onFailure expression evaluation - the
* original exception will be propagated (unless trapException is true).
* @param propagateOnSuccessEvaluationFailures
*/
public void setPropagateEvaluationFailures(boolean propagateOnSuccessEvaluationFailures) {
this.propagateOnSuccessEvaluationFailures = propagateOnSuccessEvaluationFailures;
}
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
@Override
protected Object doInvoke(ExecutionCallback callback, Object target, Message<?> message) throws Exception {
try {
Object result = callback.execute();
if (onSuccessMessageProcessor != null) {
evaluateExpression(message, this.onSuccessMessageProcessor, this.successChannel, this.propagateOnSuccessEvaluationFailures);
}
return result;
}
catch (Exception e) {
Object evalResult = evaluateExpression(message, this.onFailureMessageProcessor, this.failureChannel, false);
if (this.returnFailureExpressionResult) {
return evalResult;
}
if (!this.trapException) {
throw e;
}
return null;
}
}
private Object evaluateExpression(Message<?> message,
ExpressionEvaluatingMessageProcessor<Object> expressionEvaluatingMessageProcessor,
MessageChannel resultChannel, boolean propagateEvaluationFailure) throws Exception {
Object evalResult;
boolean evaluationFailed = false;
try {
evalResult = expressionEvaluatingMessageProcessor.processMessage(message);
}
catch (Exception e) {
evalResult = e;
evaluationFailed = true;
}
if (evalResult != null && resultChannel != null) {
message = MessageBuilder.fromMessage(message)
.setHeader(MessageHeaders.POSTPROCESS_RESULT, evalResult)
.build();
this.messagingTemplate.send(resultChannel, message);
}
if (evaluationFailed && propagateEvaluationFailure) {
throw (Exception) evalResult;
}
return evalResult;
}
}

View File

@@ -0,0 +1,95 @@
/*
* 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.handler.advice;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicInteger;
import org.springframework.integration.Message;
import org.springframework.integration.MessagingException;
/**
* A circuit breaker that stops calling a failing service after threshold
* failures, until halfOpenAfter milliseconds has elapsed. A successful
* call resets the failure counter.
*
* @author Gary Russell
* @since 2.2
*
*/
public class RequestHandlerCircuitBreakerAdvice extends AbstractRequestHandlerAdvice {
private volatile int threshold = 5;
private volatile long halfOpenAfter = 1000;
private final ConcurrentMap<Object, AdvisedMetadata> metadataMap = new ConcurrentHashMap<Object, AdvisedMetadata>();
public void setThreshold(int threshold) {
this.threshold = threshold;
}
public void setHalfOpenAfter(long halfOpenAfter) {
this.halfOpenAfter = halfOpenAfter;
}
@Override
protected Object doInvoke(ExecutionCallback callback, Object target, Message<?> message) throws Exception {
AdvisedMetadata metadata = this.metadataMap.get(target);
if (metadata == null) {
this.metadataMap.putIfAbsent(target, new AdvisedMetadata());
metadata = this.metadataMap.get(target);
}
if (metadata.getFailures().get() >= this.threshold &&
System.currentTimeMillis() - metadata.getLastFailure() < this.halfOpenAfter) {
throw new MessagingException("Circuit Breaker is Open for " + target);
}
try {
Object result = callback.execute();
if (logger.isDebugEnabled() && metadata.getFailures().get() > 0) {
logger.debug("Closing Circuit Breaker for " + target);
}
metadata.getFailures().set(0);
return result;
}
catch (Exception e) {
metadata.getFailures().incrementAndGet();
metadata.setLastFailure(System.currentTimeMillis());
throw e;
}
}
private class AdvisedMetadata {
private final AtomicInteger failures = new AtomicInteger();
private volatile long lastFailure;
private long getLastFailure() {
return lastFailure;
}
private void setLastFailure(long lastFailure) {
this.lastFailure = lastFailure;
}
private AtomicInteger getFailures() {
return failures;
}
}
}

View File

@@ -0,0 +1,87 @@
/*
* 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.handler.advice;
import org.springframework.integration.Message;
import org.springframework.integration.MessagingException;
import org.springframework.integration.handler.RetryStateGenerator;
import org.springframework.retry.RecoveryCallback;
import org.springframework.retry.RetryCallback;
import org.springframework.retry.RetryContext;
import org.springframework.retry.RetryState;
import org.springframework.retry.support.RetryTemplate;
/**
* Uses spring-retry to perform stateless or stateful retry.
* Stateless retry means the retries are performed internally
* by the {@link RetryTemplate}; stateful retry means the
* exception is thrown but state is maintained to support
* the retry policies. Stateful retry requires a
* {@link RetryStateGenerator}.
* @author Gary Russell
* @since 2.2
*
*/
public class RequestHandlerRetryAdvice extends AbstractRequestHandlerAdvice {
private volatile RetryTemplate retryTemplate = new RetryTemplate();
private volatile RecoveryCallback<Object> recoveryCallback;
// Stateless unless a state generator is provided
private volatile RetryStateGenerator retryStateGenerator =
new RetryStateGenerator() {
public RetryState determineRetryState(Message<?> message) {
return null;
}
};
public void setRetryTemplate(RetryTemplate retryTemplate) {
this.retryTemplate = retryTemplate;
}
public void setRecoveryCallback(RecoveryCallback<Object> recoveryCallback) {
this.recoveryCallback = recoveryCallback;
}
public void setRetryStateGenerator(RetryStateGenerator retryStateGenerator) {
this.retryStateGenerator = retryStateGenerator;
}
@Override
protected Object doInvoke(final ExecutionCallback callback, Object target, final Message<?> message) throws Exception {
RetryState retryState = null;
retryState = this.retryStateGenerator.determineRetryState(message);
return retryTemplate.execute(new RetryCallback<Object>(){
public Object doWithRetry(RetryContext context) throws Exception {
try {
return callback.execute();
}
catch (MessagingException e) {
if (e.getFailedMessage() == null) {
e.setFailedMessage(message);
}
throw e;
}
catch (Exception e) {
throw new MessagingException(message, "Failed to invoke handler", e);
}
}
}, this.recoveryCallback, retryState);
}
}

View File

@@ -0,0 +1,6 @@
/**
* Provides classes that are used to advise
* {@link org.springframework.integration.core.MessageHandler}s with
* cross-cutting concerns.
*/
package org.springframework.integration.handler.advice;

View File

@@ -1124,6 +1124,7 @@
</xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="request-handler-advice-chain" type="adviceChainType" minOccurs="0" maxOccurs="1" />
</xsd:sequence>
<xsd:attribute name="request-channel" type="xsd:string" use="optional">
<xsd:annotation>
@@ -1481,28 +1482,7 @@
<xsd:sequence>
<xsd:choice>
<xsd:element name="transactional" type="transactionalType" minOccurs="0" maxOccurs="1" />
<xsd:element name="advice-chain" minOccurs="0" maxOccurs="1">
<xsd:complexType>
<xsd:sequence>
<xsd:choice minOccurs="0" maxOccurs="unbounded">
<xsd:element name="ref" minOccurs="0" maxOccurs="unbounded">
<xsd:complexType>
<xsd:attribute name="bean" type="xsd:string" use="required">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="java.lang.Object" />
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
</xsd:element>
<xsd:any namespace="##other" processContents="strict" minOccurs="0" maxOccurs="unbounded" />
</xsd:choice>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element name="advice-chain" type="adviceChainType" minOccurs="0" maxOccurs="1" />
</xsd:choice>
<xsd:choice minOccurs="0" maxOccurs="1">
<xsd:element name="psuedo-transactional" type="pseudoTransactionalType" minOccurs="0" maxOccurs="1">
@@ -3422,12 +3402,34 @@ is provided, the return value is expected to match a channel name exactly.
<xsd:attributeGroup ref="transactionSyncAttributeGroup" />
</xsd:complexType>
<xsd:complexType name="adviceChainType">
<xsd:sequence>
<xsd:choice minOccurs="0" maxOccurs="unbounded">
<xsd:element name="ref" minOccurs="0" maxOccurs="unbounded">
<xsd:complexType>
<xsd:attribute name="bean" type="xsd:string" use="required">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="java.lang.Object" />
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
</xsd:element>
<xsd:any namespace="##other" processContents="strict" minOccurs="0" maxOccurs="unbounded" />
</xsd:choice>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="expressionOrInnerEndpointDefinitionAware">
<xsd:complexContent>
<xsd:extension base="handlerEndpointType">
<xsd:choice minOccurs="0" maxOccurs="2">
<xsd:choice minOccurs="0" maxOccurs="3">
<xsd:element name="poller" type="basePollerType" minOccurs="0" maxOccurs="1" />
<xsd:element name="expression" type="innerExpressionType" minOccurs="0" maxOccurs="1" />
<xsd:element name="request-handler-advice-chain" type="adviceChainType" minOccurs="0" maxOccurs="1" />
<xsd:any namespace="##other" processContents="strict" minOccurs="0" maxOccurs="1" />
</xsd:choice>
<xsd:attribute name="expression" type="xsd:string">

View File

@@ -23,7 +23,11 @@
<queue capacity="1"/>
</channel>
<filter ref="selectorBean" method="hasText" input-channel="adapterInput" output-channel="adapterOutput"/>
<filter ref="selectorBean" method="hasText" input-channel="adapterInput" output-channel="adapterOutput">
<request-handler-advice-chain>
<beans:bean class="org.springframework.integration.config.FilterParserTests$FooFilter" />
</request-handler-advice-chain>
</filter>
<beans:bean id="selectorBean"
class="org.springframework.integration.config.FilterParserTests$TestSelectorBean"/>

View File

@@ -22,7 +22,6 @@ import static org.junit.Assert.assertNull;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.integration.Message;
@@ -30,6 +29,7 @@ import org.springframework.integration.MessageChannel;
import org.springframework.integration.MessageRejectedException;
import org.springframework.integration.core.MessageSelector;
import org.springframework.integration.core.PollableChannel;
import org.springframework.integration.handler.advice.AbstractRequestHandlerAdvice;
import org.springframework.integration.message.GenericMessage;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@@ -69,13 +69,16 @@ public class FilterParserTests {
@Autowired @Qualifier("discardAndExceptionOutput")
PollableChannel discardAndExceptionOutput;
private static volatile int adviceCalled;
@Test
public void filterWithSelectorAdapterAccepts() {
adviceCalled = 0;
adapterInput.send(new GenericMessage<String>("test"));
Message<?> reply = adapterOutput.receive(0);
assertNotNull(reply);
assertEquals("test", reply.getPayload());
assertEquals(1, adviceCalled);
}
@Test
@@ -156,4 +159,13 @@ public class FilterParserTests {
}
}
public static class FooFilter extends AbstractRequestHandlerAdvice {
@Override
protected Object doInvoke(ExecutionCallback callback, Object target, Message<?> message) throws Exception {
adviceCalled++;
return callback.execute();
}
}
}

View File

@@ -22,6 +22,10 @@
<property name="name" expression="payload.sourceName"/>
<property name="age" value="42"/>
<property name="gender" expression="@testBean"/>
<request-handler-advice-chain>
<beans:bean class="org.springframework.integration.config.xml.EnricherParserTests$FooAdvice" />
</request-handler-advice-chain>
</enricher>
<beans:bean id="testBean" class="java.lang.String">

View File

@@ -35,6 +35,7 @@ import org.springframework.integration.core.PollableChannel;
import org.springframework.integration.core.SubscribableChannel;
import org.springframework.integration.endpoint.EventDrivenConsumer;
import org.springframework.integration.handler.AbstractReplyProducingMessageHandler;
import org.springframework.integration.handler.advice.AbstractRequestHandlerAdvice;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.integration.test.util.TestUtils;
import org.springframework.integration.transformer.ContentEnricher;
@@ -44,7 +45,8 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @author Mark Fisher
* @author Gunnar Hillert
*
* @author Gary Russell
*
* @since 2.1
*/
@RunWith(SpringJUnit4ClassRunner.class)
@@ -54,6 +56,7 @@ public class EnricherParserTests {
@Autowired
private ApplicationContext context;
private static volatile int adviceCalled;
@Test
@SuppressWarnings("unchecked")
@@ -84,13 +87,14 @@ public class EnricherParserTests {
throw new IllegalStateException("expected 'name', 'age', and 'gender' only, not: " + e.getKey().getExpressionString());
}
}
}
@Test
public void configurationCheckTimeoutParameters() {
Object endpoint = context.getBean("enricher");
Long requestTimeout = TestUtils.getPropertyValue(endpoint, "handler.requestTimeout", Long.class);
Long replyTimeout = TestUtils.getPropertyValue(endpoint, "handler.replyTimeout", Long.class);
@@ -98,18 +102,18 @@ public class EnricherParserTests {
assertEquals(Long.valueOf(9876L), replyTimeout);
}
@Test
public void configurationCheckRequiresReply() {
Object endpoint = context.getBean("enricher");
boolean requiresReply = TestUtils.getPropertyValue(endpoint, "handler.requiresReply", Boolean.class);
assertTrue("Was expecting requiresReply to be 'false'", requiresReply);
}
@Test
public void integrationTest() {
SubscribableChannel requests = context.getBean("requests", SubscribableChannel.class);
@@ -128,6 +132,7 @@ public class EnricherParserTests {
assertEquals(42, enriched.getAge());
assertEquals("male", enriched.getGender());
assertNotSame(original, enriched);
assertEquals(1, adviceCalled);
}
private static class Source {
@@ -176,6 +181,7 @@ public class EnricherParserTests {
this.gender = gender;
}
@Override
public Object clone() {
Target copy = new Target();
copy.setName(this.name);
@@ -184,4 +190,13 @@ public class EnricherParserTests {
}
}
public static class FooAdvice extends AbstractRequestHandlerAdvice {
@Override
protected Object doInvoke(ExecutionCallback callback, Object target, Message<?> message) throws Exception {
adviceCalled++;
return callback.execute();
}
}
}

View File

@@ -21,4 +21,9 @@
<beans:bean id="testBean" class="org.springframework.integration.config.xml.ServiceActivatorParserTests$TestBean"/>
<service-activator id="withAdvice" input-channel="advisedInput" expression="'foo'">
<request-handler-advice-chain>
<beans:bean class="org.springframework.integration.config.xml.ServiceActivatorParserTests$BarAdvice" />
</request-handler-advice-chain>
</service-activator>
</beans:beans>

View File

@@ -20,12 +20,13 @@ import static org.junit.Assert.assertEquals;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.integration.Message;
import org.springframework.integration.MessageChannel;
import org.springframework.integration.core.MessagingTemplate;
import org.springframework.integration.handler.ServiceActivatingHandler;
import org.springframework.integration.handler.advice.AbstractRequestHandlerAdvice;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@@ -56,6 +57,9 @@ public class ServiceActivatorParserTests {
@Autowired
private MessageChannel multipleArgsFromPayloadInput;
@Autowired
private MessageChannel advisedInput;
@SuppressWarnings("unused") // testing auto wiring only
@Autowired
@Qualifier("org.springframework.integration.config.ServiceActivatorFactoryBean#0")
@@ -102,6 +106,11 @@ public class ServiceActivatorParserTests {
assertEquals("JohnDoe", result);
}
@Test
public void advised() {
Object result = this.sendAndReceive(advisedInput, "hello");
assertEquals("bar", result);
}
private Object sendAndReceive(MessageChannel channel, Object payload) {
MessagingTemplate template = new MessagingTemplate(channel);
@@ -152,4 +161,13 @@ public class ServiceActivatorParserTests {
}
}
public static class BarAdvice extends AbstractRequestHandlerAdvice {
@Override
protected Object doInvoke(ExecutionCallback callback, Object target, Message<?> message) throws Exception {
callback.execute();
return "bar";
}
}
}

View File

@@ -0,0 +1,477 @@
/*
* 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.handler;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import org.aopalliance.aop.Advice;
import org.junit.Test;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.integration.Message;
import org.springframework.integration.MessageHandlingException;
import org.springframework.integration.MessageHeaders;
import org.springframework.integration.channel.QueueChannel;
import org.springframework.integration.core.PollableChannel;
import org.springframework.integration.handler.advice.ExpressionEvaluatingRequestHandlerAdvice;
import org.springframework.integration.handler.advice.RequestHandlerCircuitBreakerAdvice;
import org.springframework.integration.handler.advice.RequestHandlerRetryAdvice;
import org.springframework.integration.message.GenericMessage;
import org.springframework.retry.RecoveryCallback;
import org.springframework.retry.RetryContext;
import org.springframework.retry.RetryState;
import org.springframework.retry.support.DefaultRetryState;
/**
* @author Gary Russell
* @since 2.2
*
*/
public class AdvisedMessageHandlerTests {
@Test
public void successFailureAdvice() {
final AtomicBoolean doFail = new AtomicBoolean();
AbstractReplyProducingMessageHandler handler = new AbstractReplyProducingMessageHandler() {
@Override
protected Object handleRequestMessage(Message<?> requestMessage) {
if (doFail.get()) {
throw new RuntimeException("qux");
}
return "baz";
}
};
QueueChannel replies = new QueueChannel();
handler.setOutputChannel(replies);
Message<String> message = new GenericMessage<String>("Hello, world!");
// no advice
handler.handleMessage(message);
Message<?> reply = replies.receive(1000);
assertNotNull(reply);
assertEquals("baz", reply.getPayload());
PollableChannel successChannel = new QueueChannel();
PollableChannel failureChannel = new QueueChannel();
ExpressionEvaluatingRequestHandlerAdvice advice = new ExpressionEvaluatingRequestHandlerAdvice(
new SpelExpressionParser().parseExpression("'foo'"), successChannel,
new SpelExpressionParser().parseExpression("'bar'"), failureChannel);
List<Advice> adviceChain = new ArrayList<Advice>();
adviceChain.add(advice);
handler.setAdviceChain(adviceChain);
handler.afterPropertiesSet();
// advice with success
handler.handleMessage(message);
reply = replies.receive(1000);
assertNotNull(reply);
assertEquals("baz", reply.getPayload());
Message<?> success = successChannel.receive(1000);
assertNotNull(success);
assertEquals("Hello, world!", success.getPayload());
assertEquals("foo", success.getHeaders().get(MessageHeaders.POSTPROCESS_RESULT));
// advice with failure, not trapped
doFail.set(true);
try {
handler.handleMessage(message);
fail("Expected exception");
}
catch (Exception e) {
assertEquals("qux", e.getCause().getMessage());
}
Message<?> failure = failureChannel.receive(1000);
assertNotNull(failure);
assertEquals("Hello, world!", failure.getPayload());
assertEquals("bar", failure.getHeaders().get(MessageHeaders.POSTPROCESS_RESULT));
// advice with failure, trapped
advice.setTrapException(true);
handler.handleMessage(message);
failure = failureChannel.receive(1000);
assertNotNull(failure);
assertEquals("Hello, world!", failure.getPayload());
assertEquals("bar", failure.getHeaders().get(MessageHeaders.POSTPROCESS_RESULT));
assertNull(replies.receive(1));
// advice with failure, eval is result
advice.setReturnFailureExpressionResult(true);
handler.handleMessage(message);
failure = failureChannel.receive(1000);
assertNotNull(failure);
assertEquals("Hello, world!", failure.getPayload());
assertEquals("bar", failure.getHeaders().get(MessageHeaders.POSTPROCESS_RESULT));
reply = replies.receive(1000);
assertNotNull(reply);
assertEquals("bar", reply.getPayload());
}
@Test
public void propagateOnSuccessExpressionFailures() {
final AtomicBoolean doFail = new AtomicBoolean();
AbstractReplyProducingMessageHandler handler = new AbstractReplyProducingMessageHandler() {
@Override
protected Object handleRequestMessage(Message<?> requestMessage) {
if (doFail.get()) {
throw new RuntimeException("qux");
}
return "baz";
}
};
QueueChannel replies = new QueueChannel();
handler.setOutputChannel(replies);
Message<String> message = new GenericMessage<String>("Hello, world!");
PollableChannel successChannel = new QueueChannel();
PollableChannel failureChannel = new QueueChannel();
ExpressionEvaluatingRequestHandlerAdvice advice = new ExpressionEvaluatingRequestHandlerAdvice(
new SpelExpressionParser().parseExpression("1/0"), successChannel,
new SpelExpressionParser().parseExpression("1/0"), failureChannel);
List<Advice> adviceChain = new ArrayList<Advice>();
adviceChain.add(advice);
handler.setAdviceChain(adviceChain);
handler.afterPropertiesSet();
// failing advice with success
handler.handleMessage(message);
Message<?> reply = replies.receive(1000);
assertNotNull(reply);
assertEquals("baz", reply.getPayload());
Message<?> success = successChannel.receive(1000);
assertNotNull(success);
assertEquals("Hello, world!", success.getPayload());
assertEquals(MessageHandlingException.class, success.getHeaders().get(MessageHeaders.POSTPROCESS_RESULT).getClass());
assertEquals("Expression evaluation failed: 1/0", ((Exception) success.getHeaders().get(MessageHeaders.POSTPROCESS_RESULT)).getMessage());
// propagate failing advice with success
advice.setPropagateEvaluationFailures(true);
try {
handler.handleMessage(message);
fail("Expected Exception");
}
catch (MessageHandlingException e) {
assertEquals("Expression evaluation failed: 1/0", e.getMessage());
}
reply = replies.receive(1);
assertNull(reply);
success = successChannel.receive(1000);
assertNotNull(success);
assertEquals("Hello, world!", success.getPayload());
assertEquals(MessageHandlingException.class, success.getHeaders().get(MessageHeaders.POSTPROCESS_RESULT).getClass());
assertEquals("Expression evaluation failed: 1/0", ((Exception) success.getHeaders().get(MessageHeaders.POSTPROCESS_RESULT)).getMessage());
}
@Test
public void propagateOnFailureExpressionFailures() {
final AtomicBoolean doFail = new AtomicBoolean(true);
AbstractReplyProducingMessageHandler handler = new AbstractReplyProducingMessageHandler() {
@Override
protected Object handleRequestMessage(Message<?> requestMessage) {
if (doFail.get()) {
throw new RuntimeException("qux");
}
return "baz";
}
};
QueueChannel replies = new QueueChannel();
handler.setOutputChannel(replies);
Message<String> message = new GenericMessage<String>("Hello, world!");
PollableChannel successChannel = new QueueChannel();
PollableChannel failureChannel = new QueueChannel();
ExpressionEvaluatingRequestHandlerAdvice advice = new ExpressionEvaluatingRequestHandlerAdvice(
new SpelExpressionParser().parseExpression("1/0"), successChannel,
new SpelExpressionParser().parseExpression("1/0"), failureChannel);
List<Advice> adviceChain = new ArrayList<Advice>();
adviceChain.add(advice);
handler.setAdviceChain(adviceChain);
handler.afterPropertiesSet();
// failing advice with failure
try {
handler.handleMessage(message);
fail("Expected exception");
}
catch (Exception e) {
assertEquals("qux", e.getCause().getMessage());
}
Message<?> reply = replies.receive(1);
assertNull(reply);
Message<?> failure = failureChannel.receive(1000);
assertNotNull(failure);
assertEquals("Hello, world!", failure.getPayload());
assertEquals(MessageHandlingException.class, failure.getHeaders().get(MessageHeaders.POSTPROCESS_RESULT).getClass());
assertEquals("Expression evaluation failed: 1/0", ((Exception) failure.getHeaders().get(MessageHeaders.POSTPROCESS_RESULT)).getMessage());
// propagate failing advice with failure; expect original exception
advice.setPropagateEvaluationFailures(true);
try {
handler.handleMessage(message);
fail("Expected Exception");
}
catch (MessageHandlingException e) {
assertEquals("qux", e.getCause().getMessage());
}
reply = replies.receive(1);
assertNull(reply);
failure = failureChannel.receive(1000);
assertNotNull(failure);
assertEquals("Hello, world!", failure.getPayload());
assertEquals(MessageHandlingException.class, failure.getHeaders().get(MessageHeaders.POSTPROCESS_RESULT).getClass());
assertEquals("Expression evaluation failed: 1/0", ((Exception) failure.getHeaders().get(MessageHeaders.POSTPROCESS_RESULT)).getMessage());
}
@Test
public void circuitBreakerTests() throws Exception {
final AtomicBoolean doFail = new AtomicBoolean();
AbstractReplyProducingMessageHandler handler = new AbstractReplyProducingMessageHandler() {
@Override
protected Object handleRequestMessage(Message<?> requestMessage) {
if (doFail.get()) {
throw new RuntimeException("foo");
}
return "bar";
}
};
handler.setBeanName("baz");
handler.setOutputChannel(new QueueChannel());
RequestHandlerCircuitBreakerAdvice advice = new RequestHandlerCircuitBreakerAdvice();
/*
* Circuit breaker opens after 2 failures; allows a new attempt after 100ms and
* immediately opens again if that attempt fails. After a successful attempt,
* we reset the failure counter.
*/
advice.setThreshold(2);
advice.setHalfOpenAfter(100);
List<Advice> adviceChain = new ArrayList<Advice>();
adviceChain.add(advice);
handler.setAdviceChain(adviceChain);
handler.afterPropertiesSet();
doFail.set(true);
Message<String> message = new GenericMessage<String>("Hello, world!");
try {
handler.handleMessage(message);
fail("Expected failure");
}
catch (Exception e) {
assertEquals("foo", e.getCause().getMessage());
}
try {
handler.handleMessage(message);
fail("Expected failure");
}
catch (Exception e) {
assertEquals("foo", e.getCause().getMessage());
}
try {
handler.handleMessage(message);
fail("Expected failure");
}
catch (Exception e) {
assertEquals("Circuit Breaker is Open for baz", e.getMessage());
}
Thread.sleep(100);
try {
handler.handleMessage(message);
fail("Expected failure");
}
catch (Exception e) {
assertEquals("foo", e.getCause().getMessage());
}
try {
handler.handleMessage(message);
fail("Expected failure");
}
catch (Exception e) {
assertEquals("Circuit Breaker is Open for baz", e.getMessage());
}
Thread.sleep(100);
doFail.set(false);
handler.handleMessage(message);
doFail.set(true);
try {
handler.handleMessage(message);
fail("Expected failure");
}
catch (Exception e) {
assertEquals("foo", e.getCause().getMessage());
}
try {
handler.handleMessage(message);
fail("Expected failure");
}
catch (Exception e) {
assertEquals("foo", e.getCause().getMessage());
}
try {
handler.handleMessage(message);
fail("Expected failure");
}
catch (Exception e) {
assertEquals("Circuit Breaker is Open for baz", e.getMessage());
}
}
@Test
public void defaultRetrySucceedonThirdTry() {
final AtomicInteger counter = new AtomicInteger(2);
AbstractReplyProducingMessageHandler handler = new AbstractReplyProducingMessageHandler() {
@Override
protected Object handleRequestMessage(Message<?> requestMessage) {
if (counter.getAndDecrement() > 0) {
throw new RuntimeException("foo");
}
return "bar";
}
};
QueueChannel replies = new QueueChannel();
handler.setOutputChannel(replies);
RequestHandlerRetryAdvice advice = new RequestHandlerRetryAdvice();
List<Advice> adviceChain = new ArrayList<Advice>();
adviceChain.add(advice);
handler.setAdviceChain(adviceChain);
handler.afterPropertiesSet();
Message<String> message = new GenericMessage<String>("Hello, world!");
handler.handleMessage(message);
assertTrue(counter.get() == -1);
Message<?> reply = replies.receive(1000);
assertNotNull(reply);
assertEquals("bar", reply.getPayload());
}
@Test
public void defaultStatefulRetrySucceedonThirdTry() {
final AtomicInteger counter = new AtomicInteger(2);
AbstractReplyProducingMessageHandler handler = new AbstractReplyProducingMessageHandler() {
@Override
protected Object handleRequestMessage(Message<?> requestMessage) {
if (counter.getAndDecrement() > 0) {
throw new RuntimeException("foo");
}
return "bar";
}
};
QueueChannel replies = new QueueChannel();
handler.setOutputChannel(replies);
RequestHandlerRetryAdvice advice = new RequestHandlerRetryAdvice();
advice.setRetryStateGenerator(new RetryStateGenerator() {
public RetryState determineRetryState(Message<?> message) {
return new DefaultRetryState(message.getHeaders().getId());
}
});
List<Advice> adviceChain = new ArrayList<Advice>();
adviceChain.add(advice);
handler.setAdviceChain(adviceChain);
handler.afterPropertiesSet();
Message<String> message = new GenericMessage<String>("Hello, world!");
for (int i = 0; i < 3; i++) {
try {
handler.handleMessage(message);
}
catch (Exception e) {
assertTrue(i < 2);
}
}
assertTrue(counter.get() == -1);
Message<?> reply = replies.receive(1000);
assertNotNull(reply);
assertEquals("bar", reply.getPayload());
}
@Test
public void defaultStatefulRetryRecoverAfterThirdTry() {
final AtomicInteger counter = new AtomicInteger(3);
AbstractReplyProducingMessageHandler handler = new AbstractReplyProducingMessageHandler() {
@Override
protected Object handleRequestMessage(Message<?> requestMessage) {
if (counter.getAndDecrement() > 0) {
throw new RuntimeException("foo");
}
return "bar";
}
};
QueueChannel replies = new QueueChannel();
handler.setOutputChannel(replies);
RequestHandlerRetryAdvice advice = new RequestHandlerRetryAdvice();
advice.setRetryStateGenerator(new RetryStateGenerator() {
public RetryState determineRetryState(Message<?> message) {
return new DefaultRetryState(message.getHeaders().getId());
}
});
advice.setRecoveryCallback(new RecoveryCallback<Object>() {
public Object recover(RetryContext context) throws Exception {
return "baz";
}
});
List<Advice> adviceChain = new ArrayList<Advice>();
adviceChain.add(advice);
handler.setAdviceChain(adviceChain);
handler.afterPropertiesSet();
Message<String> message = new GenericMessage<String>("Hello, world!");
for (int i = 0; i < 4; i++) {
try {
handler.handleMessage(message);
}
catch (Exception e) {
}
}
assertTrue(counter.get() == 0);
Message<?> reply = replies.receive(1000);
assertNotNull(reply);
assertEquals("baz", reply.getPayload());
}
}

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.
@@ -19,19 +19,22 @@ package org.springframework.integration.transformer;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
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.core.PollableChannel;
import org.springframework.integration.handler.advice.AbstractRequestHandlerAdvice;
import org.springframework.integration.message.GenericMessage;
/**
* @author Mark Fisher
* @author Gary Russell
*/
public class TransformerContextTests {
private static volatile int adviceCalled;
@Test
public void methodInvokingTransformer() {
ApplicationContext context = new ClassPathXmlApplicationContext(
@@ -41,6 +44,16 @@ public class TransformerContextTests {
input.send(new GenericMessage<String>("foo"));
Message<?> reply = output.receive(0);
assertEquals("FOO", reply.getPayload());
assertEquals(1, adviceCalled);
}
public static class FooAdvice extends AbstractRequestHandlerAdvice {
@Override
protected Object doInvoke(ExecutionCallback callback, Object target, Message<?> message) throws Exception {
adviceCalled++;
return callback.execute();
}
}
}

View File

@@ -13,7 +13,11 @@
<queue capacity="50"/>
</channel>
<transformer input-channel="input" ref="testBean" method="upperCase" output-channel="output"/>
<transformer input-channel="input" ref="testBean" method="upperCase" output-channel="output">
<request-handler-advice-chain>
<beans:bean class="org.springframework.integration.transformer.TransformerContextTests$FooAdvice" />
</request-handler-advice-chain>
</transformer>
<beans:bean id="testBean" class="org.springframework.integration.transformer.TestBean"/>

View File

@@ -81,9 +81,10 @@
</xsd:documentation>
</xsd:annotation>
<xsd:complexType>
<xsd:all>
<xsd:choice minOccurs="0" maxOccurs="2">
<xsd:element ref="integration:poller" minOccurs="0" maxOccurs="1" />
</xsd:all>
<xsd:element name="request-handler-advice-chain" type="integration:adviceChainType" minOccurs="0" maxOccurs="1" />
</xsd:choice>
<xsd:attribute name="id" type="xsd:string" />
<xsd:attribute name="channel" type="xsd:string">
<xsd:annotation>

View File

@@ -11,6 +11,14 @@
<int-event:outbound-channel-adapter id="eventAdapter" channel="input"/>
<int:channel id="inputAdvice"/>
<int-event:outbound-channel-adapter id="withAdvice" channel="inputAdvice">
<int-event:request-handler-advice-chain>
<bean class="org.springframework.integration.event.config.EventOutboundChannelAdapterParserTests$FooAdvice" />
</int-event:request-handler-advice-chain>
</int-event:outbound-channel-adapter>
<int:chain input-channel="inputChain">
<int:transformer expression="payload + 'bar'"/>
<int-event:outbound-channel-adapter/>

View File

@@ -16,7 +16,11 @@
package org.springframework.integration.event.config;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import junit.framework.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.DirectFieldAccessor;
@@ -31,16 +35,15 @@ import org.springframework.integration.channel.QueueChannel;
import org.springframework.integration.core.MessageHandler;
import org.springframework.integration.endpoint.EventDrivenConsumer;
import org.springframework.integration.event.outbound.ApplicationEventPublishingMessageHandler;
import org.springframework.integration.handler.advice.AbstractRequestHandlerAdvice;
import org.springframework.integration.message.GenericMessage;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
/**
* @author Oleg Zhurakousky
* @author Artem Bilan
* @author Gary Russell
* @since 2.0
*/
@RunWith(SpringJUnit4ClassRunner.class)
@@ -52,6 +55,7 @@ public class EventOutboundChannelAdapterParserTests {
private volatile boolean receivedEvent;
private static volatile int adviceCalled;
@Test
public void validateEventParser() {
@@ -82,8 +86,30 @@ public class EventOutboundChannelAdapterParserTests {
Assert.assertTrue(receivedEvent);
}
@Test
public void withAdvice() {
receivedEvent = false;
ApplicationListener<?> listener = new ApplicationListener<ApplicationEvent>() {
public void onApplicationEvent(ApplicationEvent event) {
Object source = event.getSource();
if (source instanceof Message){
String payload = (String) ((Message<?>) source).getPayload();
if (payload.equals("hello")) {
receivedEvent = true;
}
}
}
};
context.addApplicationListener(listener);
DirectChannel channel = context.getBean("inputAdvice", DirectChannel.class);
channel.send(new GenericMessage<String>("hello"));
Assert.assertTrue(receivedEvent);
Assert.assertEquals(1, adviceCalled);
}
@Test //INT-2275
public void testInsideChain() {
receivedEvent = false;
ApplicationListener<?> listener = new ApplicationListener<ApplicationEvent>() {
public void onApplicationEvent(ApplicationEvent event) {
Object source = event.getSource();
@@ -103,6 +129,7 @@ public class EventOutboundChannelAdapterParserTests {
@Test(timeout=2000)
public void validateUsageWithPollableChannel() throws Exception {
receivedEvent = false;
ConfigurableApplicationContext context = new ClassPathXmlApplicationContext("EventOutboundChannelAdapterParserTestsWithPollable-context.xml", EventOutboundChannelAdapterParserTests.class);
final CyclicBarrier barier = new CyclicBarrier(2);
ApplicationListener<?> listener = new ApplicationListener<ApplicationEvent>() {
@@ -132,4 +159,13 @@ public class EventOutboundChannelAdapterParserTests {
Assert.assertTrue(receivedEvent);
}
public static class FooAdvice extends AbstractRequestHandlerAdvice {
@Override
protected Object doInvoke(ExecutionCallback callback, Object target, Message<?> message) throws Exception {
adviceCalled++;
return callback.execute();
}
}
}

View File

@@ -215,6 +215,8 @@ public class FileWritingMessageHandler extends AbstractReplyProducingMessageHand
@Override
public final void onInit() {
super.onInit();
this.evaluationContext.addPropertyAccessor(new MapAccessor());
final BeanFactory beanFactory = this.getBeanFactory();

View File

@@ -16,17 +16,16 @@
package org.springframework.integration.file.config;
import org.w3c.dom.Element;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.integration.config.xml.AbstractOutboundChannelAdapterParser;
import org.w3c.dom.Element;
/**
* Parser for the &lt;outbound-channel-adapter/&gt; element of the 'file'
* namespace.
*
*
* @author Mark Fisher
* @author Iwein Fuld
* @author Oleg Zhurakousky
@@ -40,4 +39,10 @@ public class FileOutboundChannelAdapterParser extends AbstractOutboundChannelAda
return handlerBuilder.getBeanDefinition();
}
@Override
protected boolean isUsingReplyProducer() {
// cannot be automatically determined by superclass because we are using a factory bean.
return true;
}
}

View File

@@ -255,9 +255,10 @@ Only files matching this regular expression will be picked up by this adapter.
</xsd:element>
<xsd:complexType name="outboundFileBaseType">
<xsd:sequence>
<xsd:choice minOccurs="0" maxOccurs="2">
<xsd:element ref="integration:poller" minOccurs="0" maxOccurs="1"/>
</xsd:sequence>
<xsd:element name="request-handler-advice-chain" type="integration:adviceChainType" minOccurs="0" maxOccurs="1" />
</xsd:choice>
<xsd:attribute name="id" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[Identifies the underlying Spring bean definition (EventDrivenConsumer)]]></xsd:documentation>

View File

@@ -47,7 +47,11 @@
<file:outbound-channel-adapter id="usageChannel"
filename-generator-expression="'fileToAppend.txt'"
mode="APPEND"
directory="test"/>
directory="test">
<file:request-handler-advice-chain>
<bean class="org.springframework.integration.file.config.FileOutboundChannelAdapterParserTests$FooAdvice" />
</file:request-handler-advice-chain>
</file:outbound-channel-adapter>
<file:outbound-channel-adapter id="usageChannelWithFailMode"
filename-generator-expression="'fileToAppend.txt'"

View File

@@ -29,15 +29,16 @@ import java.nio.charset.Charset;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.DirectFieldAccessor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.expression.Expression;
import org.springframework.integration.Message;
import org.springframework.integration.MessageChannel;
import org.springframework.integration.MessagingException;
import org.springframework.expression.Expression;
import org.springframework.integration.endpoint.EventDrivenConsumer;
import org.springframework.integration.file.DefaultFileNameGenerator;
import org.springframework.integration.file.FileWritingMessageHandler;
import org.springframework.integration.handler.advice.AbstractRequestHandlerAdvice;
import org.springframework.integration.message.GenericMessage;
import org.springframework.integration.test.util.TestUtils;
import org.springframework.test.context.ContextConfiguration;
@@ -88,6 +89,8 @@ public class FileOutboundChannelAdapterParserTests {
@Autowired
MessageChannel usageChannelConcurrent;
private volatile static int adviceCalled;
@Test
public void simpleAdapter() {
DirectFieldAccessor adapterAccessor = new DirectFieldAccessor(simpleAdapter);
@@ -272,5 +275,14 @@ public class FileOutboundChannelAdapterParserTests {
for (char character : characters) {
assertEquals(c, character);
}
}
public static class FooAdvice extends AbstractRequestHandlerAdvice {
@Override
protected Object doInvoke(ExecutionCallback callback, Object target, Message<?> message) throws Exception {
adviceCalled++;
return callback.execute();
}
}
}

View File

@@ -19,7 +19,11 @@
<int-file:outbound-gateway id="gatewayWithDirectoryExpression"
request-channel="someChannel" directory-expression="'build/foo'"
auto-startup="false" order="777" filename-generator-expression="'foo.txt'" />
auto-startup="false" order="777" filename-generator-expression="'foo.txt'">
<int-file:request-handler-advice-chain>
<beans:bean class="org.springframework.integration.file.config.FileOutboundGatewayParserTests$FooAdvice" />
</int-file:request-handler-advice-chain>
</int-file:outbound-gateway>
<int-file:outbound-gateway id="gatewayWithReplaceMode"
request-channel="gatewayWithReplaceModeChannel"

View File

@@ -35,6 +35,7 @@ import org.springframework.integration.core.MessagingTemplate;
import org.springframework.integration.endpoint.EventDrivenConsumer;
import org.springframework.integration.file.DefaultFileNameGenerator;
import org.springframework.integration.file.FileWritingMessageHandler;
import org.springframework.integration.handler.advice.AbstractRequestHandlerAdvice;
import org.springframework.integration.message.GenericMessage;
import org.springframework.integration.test.util.TestUtils;
import org.springframework.test.context.ContextConfiguration;
@@ -70,6 +71,8 @@ public class FileOutboundGatewayParserTests {
@Autowired
MessageChannel gatewayWithFailModeLowercaseChannel;
private volatile static int adviceCalled;
@Test
public void checkOrderedGateway() throws Exception {
@@ -94,6 +97,8 @@ public class FileOutboundGatewayParserTests {
public void testOutboundGatewayWithDirectoryExpression() throws Exception {
FileWritingMessageHandler handler = TestUtils.getPropertyValue(gatewayWithDirectoryExpression, "handler", FileWritingMessageHandler.class);
assertEquals("'build/foo'", TestUtils.getPropertyValue(handler, "destinationDirectoryExpression", Expression.class).getExpressionString());
handler.handleMessage(new GenericMessage<String>("foo"));
assertEquals(1, adviceCalled);
}
/**
@@ -282,4 +287,13 @@ public class FileOutboundGatewayParserTests {
}
public static class FooAdvice extends AbstractRequestHandlerAdvice {
@Override
protected Object doInvoke(ExecutionCallback callback, Object target, Message<?> message) throws Exception {
adviceCalled++;
return null;
}
}
}

View File

@@ -20,6 +20,9 @@
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="base-ftp-adapter-type">
<xsd:all>
<xsd:element name="request-handler-advice-chain" type="integration:adviceChainType" minOccurs="0" maxOccurs="1" />
</xsd:all>
<xsd:attribute name="remote-directory-expression"
type="xsd:string">
<xsd:annotation>
@@ -232,6 +235,9 @@
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="base-adapter-type">
<xsd:all>
<xsd:element name="request-handler-advice-chain" type="integration:adviceChainType" minOccurs="0" maxOccurs="1" />
</xsd:all>
<xsd:attribute name="command" use="required" type="xsd:string">
<xsd:annotation>
<xsd:documentation>

View File

@@ -59,7 +59,16 @@
channel="ftpChannel"
session-factory="cachingSessionFactory"
remote-directory="foo/bar"/>
<int-ftp:outbound-channel-adapter id="advisedAdapter"
channel="ftpChannel"
session-factory="cachingSessionFactory"
remote-directory="foo/bar">
<int-ftp:request-handler-advice-chain>
<bean class="org.springframework.integration.ftp.config.FtpOutboundChannelAdapterParserTests$FooAdvice" />
</int-ftp:request-handler-advice-chain>
</int-ftp:outbound-channel-adapter>
<int:channel id="anotherFtpChannel"/>
<int:publish-subscribe-channel id="ftpChannel"/>

View File

@@ -26,16 +26,18 @@ import java.util.Iterator;
import java.util.Set;
import org.junit.Test;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.integration.Message;
import org.springframework.integration.channel.PublishSubscribeChannel;
import org.springframework.integration.core.MessageHandler;
import org.springframework.integration.endpoint.EventDrivenConsumer;
import org.springframework.integration.file.remote.handler.FileTransferringMessageHandler;
import org.springframework.integration.file.remote.session.CachingSessionFactory;
import org.springframework.integration.ftp.session.DefaultFtpSessionFactory;
import org.springframework.integration.handler.advice.AbstractRequestHandlerAdvice;
import org.springframework.integration.message.GenericMessage;
import org.springframework.integration.test.util.TestUtils;
/**
@@ -45,6 +47,8 @@ import org.springframework.integration.test.util.TestUtils;
*/
public class FtpOutboundChannelAdapterParserTests {
private static volatile int adviceCalled;
@Test
public void testFtpOutboundChannelAdapterComplete() throws Exception{
ApplicationContext ac =
@@ -97,6 +101,15 @@ public class FtpOutboundChannelAdapterParserTests {
assertEquals(DefaultFtpSessionFactory.class, innerSfProperty.getClass());
}
@Test
public void adviceChain() {
ApplicationContext ac = new ClassPathXmlApplicationContext(
"FtpOutboundChannelAdapterParserTests-context.xml", this.getClass());
Object adapter = ac.getBean("advisedAdapter");
MessageHandler handler = TestUtils.getPropertyValue(adapter, "handler", MessageHandler.class);
handler.handleMessage(new GenericMessage<String>("foo"));
assertEquals(1, adviceCalled);
}
@Test
public void testTemporaryFileSuffix() {
@@ -107,4 +120,13 @@ public class FtpOutboundChannelAdapterParserTests {
assertFalse((Boolean)TestUtils.getPropertyValue(handler,"useTemporaryFileName"));
}
public static class FooAdvice extends AbstractRequestHandlerAdvice {
@Override
protected Object doInvoke(ExecutionCallback callback, Object target, Message<?> message) throws Exception {
adviceCalled++;
return null;
}
}
}

View File

@@ -41,7 +41,11 @@
command-options="-P"
expression="payload"
order="2"
/>
>
<int-ftp:request-handler-advice-chain>
<bean class="org.springframework.integration.ftp.config.FtpOutboundGatewayParserTests$FooAdvice" />
</int-ftp:request-handler-advice-chain>
</int-ftp:outbound-gateway>
<int:channel id="outbound"/>

View File

@@ -26,9 +26,12 @@ import java.util.Set;
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.endpoint.AbstractEndpoint;
import org.springframework.integration.file.remote.session.CachingSessionFactory;
import org.springframework.integration.ftp.gateway.FtpOutboundGateway;
import org.springframework.integration.handler.advice.AbstractRequestHandlerAdvice;
import org.springframework.integration.message.GenericMessage;
import org.springframework.integration.test.util.TestUtils;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@@ -50,6 +53,8 @@ public class FtpOutboundGatewayParserTests {
@Autowired
AbstractEndpoint gateway2;
private static volatile int adviceCalled;
@Test
public void testGateway1() {
FtpOutboundGateway gateway = TestUtils.getPropertyValue(gateway1,
@@ -84,5 +89,17 @@ public class FtpOutboundGatewayParserTests {
@SuppressWarnings("unchecked")
Set<String> options = TestUtils.getPropertyValue(gateway, "options", Set.class);
assertTrue(options.contains("-P"));
gateway.handleMessage(new GenericMessage<String>("foo"));
assertEquals(1, adviceCalled);
}
public static class FooAdvice extends AbstractRequestHandlerAdvice {
@Override
protected Object doInvoke(ExecutionCallback callback, Object target, Message<?> message) throws Exception {
adviceCalled++;
return null;
}
}
}

View File

@@ -132,7 +132,7 @@
writes Message to a Gemfire cache
</xsd:documentation>
</xsd:annotation>
<xsd:sequence>
<xsd:choice minOccurs="0" maxOccurs="2">
<xsd:element name="cache-entries" type="beans:mapType"
minOccurs="0" maxOccurs="1">
<xsd:annotation>
@@ -142,7 +142,8 @@
</xsd:documentation>
</xsd:annotation>
</xsd:element>
</xsd:sequence>
<xsd:element name="request-handler-advice-chain" type="integration:adviceChainType" minOccurs="0" maxOccurs="1" />
</xsd:choice>
<xsd:attribute name="id" type="xsd:string" use="optional" />

View File

@@ -0,0 +1,21 @@
<?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-gfe="http://www.springframework.org/schema/integration/gemfire"
xmlns:int="http://www.springframework.org/schema/integration"
xsi:schemaLocation="http://www.springframework.org/schema/integration/gemfire http://www.springframework.org/schema/integration/gemfire/spring-integration-gemfire-2.2.xsd
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<int:channel id="input" />
<int-gfe:outbound-channel-adapter region="region" channel="input">
<int-gfe:request-handler-advice-chain>
<bean class="org.springframework.integration.gemfire.config.xml.GemfireOutboundChannelAdapterParserTests$FooAdvice" />
</int-gfe:request-handler-advice-chain>
</int-gfe:outbound-channel-adapter>
<bean id="region" class="org.mockito.Mockito" factory-method="mock">
<constructor-arg value="com.gemstone.gemfire.cache.Region" />
</bean>
</beans>

View File

@@ -13,24 +13,51 @@
package org.springframework.integration.gemfire.config.xml;
import org.junit.Test;
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
import org.w3c.dom.Element;
import static org.junit.Assert.assertEquals;
import static org.springframework.integration.gemfire.config.xml.ParserTestUtil.createFakeParserContext;
import static org.springframework.integration.gemfire.config.xml.ParserTestUtil.loadXMLFrom;
import org.junit.Test;
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.handler.advice.AbstractRequestHandlerAdvice;
import org.springframework.integration.message.GenericMessage;
import org.w3c.dom.Element;
/**
* @author Dan Oxlade
*/
public class GemfireOutboundChannelAdapterParserTests {
private GemfireOutboundChannelAdapterParser underTest = new GemfireOutboundChannelAdapterParser();
private GemfireOutboundChannelAdapterParser underTest = new GemfireOutboundChannelAdapterParser();
@Test(expected = BeanDefinitionParsingException.class)
public void regionIsARequiredAttribute() throws Exception {
String xml = "<outbound-channel-adapter />";
Element element = loadXMLFrom(xml).getDocumentElement();
underTest.parseConsumer(element, createFakeParserContext());
}
private volatile static int adviceCalled;
@Test(expected = BeanDefinitionParsingException.class)
public void regionIsARequiredAttribute() throws Exception {
String xml = "<outbound-channel-adapter />";
Element element = loadXMLFrom(xml).getDocumentElement();
underTest.parseConsumer(element, createFakeParserContext());
}
@Test
public void withAdvice() {
ApplicationContext ctx = new ClassPathXmlApplicationContext(this.getClass().getSimpleName() + "-context.xml", this.getClass());
MessageChannel channel = ctx.getBean("input", MessageChannel.class);
channel.send(new GenericMessage<String>("foo"));
assertEquals(1, adviceCalled);
}
public static class FooAdvice extends AbstractRequestHandlerAdvice {
@Override
protected Object doInvoke(ExecutionCallback callback, Object target, Message<?> message) throws Exception {
adviceCalled++;
return null;
}
}
}

View File

@@ -49,10 +49,11 @@
]]></xsd:documentation>
</xsd:annotation>
<xsd:complexType>
<xsd:all minOccurs="0" maxOccurs="1">
<xsd:choice minOccurs="0" maxOccurs="2">
<xsd:element ref="integration:poller" minOccurs="0"
maxOccurs="1" />
</xsd:all>
<xsd:element name="request-handler-advice-chain" type="integration:adviceChainType" minOccurs="0" maxOccurs="1" />
</xsd:choice>
<xsd:attributeGroup ref="integration:inputOutputChannelGroupWithId" />
<xsd:attribute name="customizer" type="xsd:string">
<xsd:annotation>

View File

@@ -21,7 +21,11 @@
</beans:bean>
<groovy:control-bus input-channel="input" output-channel="output" send-timeout="100" order="1" auto-startup="true"
customizer="groovyCustomizer"/>
customizer="groovyCustomizer">
<groovy:request-handler-advice-chain>
<beans:bean class="org.springframework.integration.groovy.config.GroovyControlBusTests$FooAdvice" />
</groovy:request-handler-advice-chain>
</groovy:control-bus>
<beans:bean id="service" class="org.springframework.integration.groovy.config.GroovyControlBusTests$Service" />

View File

@@ -20,12 +20,11 @@ import static junit.framework.Assert.assertTrue;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.fail;
import groovy.lang.GroovyObject;
import java.util.HashMap;
import java.util.Map;
import groovy.lang.GroovyObject;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.BeanCreationException;
@@ -36,6 +35,7 @@ import org.springframework.integration.Message;
import org.springframework.integration.MessageChannel;
import org.springframework.integration.MessageHandlingException;
import org.springframework.integration.core.PollableChannel;
import org.springframework.integration.handler.advice.AbstractRequestHandlerAdvice;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.jmx.export.annotation.ManagedOperation;
import org.springframework.jmx.export.annotation.ManagedResource;
@@ -49,6 +49,7 @@ import org.springframework.web.context.request.RequestContextHolder;
/**
* @author Dave Syer
* @author Artem Bilan
* @author Gary Russell
* @since 2.0
*/
@ContextConfiguration
@@ -64,6 +65,8 @@ public class GroovyControlBusTests {
@Autowired
private MyGroovyCustomizer groovyCustomizer;
private static volatile int adviceCalled;
@Test
public void testOperationOfControlBus() { // long is > 3
this.groovyCustomizer.executed = false;
@@ -72,6 +75,7 @@ public class GroovyControlBusTests {
assertEquals("catbar", output.receive(0).getPayload());
assertNull(output.receive(0));
assertTrue(this.groovyCustomizer.executed);
assertEquals(1, adviceCalled);
}
@Test //INT-2567
@@ -201,4 +205,13 @@ public class GroovyControlBusTests {
}
public static class FooAdvice extends AbstractRequestHandlerAdvice {
@Override
protected Object doInvoke(ExecutionCallback callback, Object target, Message<?> message) throws Exception {
adviceCalled++;
return callback.execute();
}
}
}

View File

@@ -1,11 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns="http://www.springframework.org/schema/integration/http" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:beans="http://www.springframework.org/schema/beans" xmlns:tool="http://www.springframework.org/schema/tool"
xmlns:integration="http://www.springframework.org/schema/integration"
targetNamespace="http://www.springframework.org/schema/integration/http" elementFormDefault="qualified"
attributeFormDefault="unqualified">
<xsd:import namespace="http://www.springframework.org/schema/beans" />
<xsd:import namespace="http://www.springframework.org/schema/tool" />
<xsd:import namespace="http://www.springframework.org/schema/integration" schemaLocation="http://www.springframework.org/schema/integration/spring-integration-2.2.xsd" />
<xsd:annotation>
<xsd:documentation><![CDATA[
@@ -347,9 +349,10 @@ The String "HTTP_REQUEST_HEADERS" will match against any of the standard HTTP Re
Defines an outbound HTTP-based Channel Adapter.
</xsd:documentation>
</xsd:annotation>
<xsd:sequence>
<xsd:choice minOccurs="0" maxOccurs="2">
<xsd:element name="uri-variable" type="uriVariableType" minOccurs="0" maxOccurs="unbounded" />
</xsd:sequence>
<xsd:element name="request-handler-advice-chain" type="integration:adviceChainType" minOccurs="0" maxOccurs="1" />
</xsd:choice>
<xsd:attribute name="id" type="xsd:string" />
<xsd:attribute name="url" type="xsd:string" use="optional">
<xsd:annotation>
@@ -509,9 +512,10 @@ The String "HTTP_REQUEST_HEADERS" will match against any of the standard HTTP Re
</xsd:annotation>
<xsd:complexContent>
<xsd:extension base="gatewayType">
<xsd:sequence>
<xsd:choice minOccurs="0" maxOccurs="2">
<xsd:element name="uri-variable" type="uriVariableType" minOccurs="0" maxOccurs="unbounded" />
</xsd:sequence>
<xsd:element name="request-handler-advice-chain" type="integration:adviceChainType" minOccurs="0" maxOccurs="1" />
</xsd:choice>
<xsd:attribute name="request-channel" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>

View File

@@ -40,6 +40,12 @@
<outbound-channel-adapter id="withUrlExpression" url-expression="'http://localhost/test1'" channel="requests"/>
<outbound-channel-adapter id="withAdvice" url-expression="'http://localhost/test1'" channel="requests">
<request-handler-advice-chain>
<beans:bean class="org.springframework.integration.http.config.HttpOutboundChannelAdapterParserTests$FooAdvice" />
</request-handler-advice-chain>
</outbound-channel-adapter>
<outbound-channel-adapter id="withUrlExpressionAndTemplate"
url-expression="'http://localhost/test1'" channel="requests"
rest-template="customRestTemplate"/>

View File

@@ -28,7 +28,6 @@ import java.util.Map;
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.annotation.Qualifier;
@@ -41,8 +40,12 @@ import org.springframework.http.HttpMethod;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.integration.Message;
import org.springframework.integration.core.MessageHandler;
import org.springframework.integration.endpoint.AbstractEndpoint;
import org.springframework.integration.handler.advice.AbstractRequestHandlerAdvice;
import org.springframework.integration.http.outbound.HttpRequestExecutingMessageHandler;
import org.springframework.integration.message.GenericMessage;
import org.springframework.integration.test.util.TestUtils;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@@ -52,6 +55,7 @@ import org.springframework.web.client.RestTemplate;
/**
* @author Mark Fisher
* @author Gary Russell
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@@ -75,12 +79,16 @@ public class HttpOutboundChannelAdapterParserTests {
@Autowired @Qualifier("withUrlExpression")
private AbstractEndpoint withUrlExpression;
@Autowired @Qualifier("withAdvice")
private AbstractEndpoint withAdvice;
@Autowired @Qualifier("withUrlExpressionAndTemplate")
private AbstractEndpoint withUrlExpressionAndTemplate;
@Autowired
private ApplicationContext applicationContext;
private static volatile int adviceCalled;
@Test
public void minimalConfig() {
@@ -201,6 +209,13 @@ public class HttpOutboundChannelAdapterParserTests {
assertEquals(true, handlerAccessor.getPropertyValue("extractPayload"));
}
@Test
public void withAdvice() {
MessageHandler handler = TestUtils.getPropertyValue(this.withAdvice, "handler", MessageHandler.class);
handler.handleMessage(new GenericMessage<String>("foo"));
assertEquals(1, adviceCalled);
}
@Test
public void withUrlExpressionAndTemplate() {
DirectFieldAccessor endpointAccessor = new DirectFieldAccessor(this.withUrlExpressionAndTemplate);
@@ -239,4 +254,13 @@ public class HttpOutboundChannelAdapterParserTests {
}
}
public static class FooAdvice extends AbstractRequestHandlerAdvice {
@Override
protected Object doInvoke(ExecutionCallback callback, Object target, Message<?> message) throws Exception {
adviceCalled++;
return null;
}
}
}

View File

@@ -42,6 +42,12 @@
<outbound-gateway id="withUrlExpression" url-expression="'http://localhost/test1'" request-channel="requests"/>
<outbound-gateway id="withAdvice" url-expression="'http://localhost/test1'" request-channel="requests">
<request-handler-advice-chain>
<beans:bean class="org.springframework.integration.http.config.HttpOutboundGatewayParserTests$FooAdvice" />
</request-handler-advice-chain>
</outbound-gateway>
<beans:bean id="testRequestFactory" class="org.springframework.http.client.SimpleClientHttpRequestFactory"/>
<beans:bean id="testErrorHandler" class="org.springframework.integration.http.config.HttpOutboundGatewayParserTests$StubErrorHandler"/>

View File

@@ -26,7 +26,6 @@ import java.util.Map;
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.annotation.Qualifier;
@@ -37,9 +36,12 @@ import org.springframework.http.HttpMethod;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.integration.Message;
import org.springframework.integration.MessageChannel;
import org.springframework.integration.endpoint.AbstractEndpoint;
import org.springframework.integration.handler.advice.AbstractRequestHandlerAdvice;
import org.springframework.integration.http.outbound.HttpRequestExecutingMessageHandler;
import org.springframework.integration.message.GenericMessage;
import org.springframework.integration.test.util.TestUtils;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@@ -48,6 +50,7 @@ import org.springframework.web.client.ResponseErrorHandler;
/**
* @author Mark Fisher
* @author Gary Russell
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@@ -62,9 +65,13 @@ public class HttpOutboundGatewayParserTests {
@Autowired @Qualifier("withUrlExpression")
private AbstractEndpoint withUrlExpressionEndpoint;
@Autowired @Qualifier("withAdvice")
private AbstractEndpoint withAdvice;
@Autowired
private ApplicationContext applicationContext;
private static volatile int adviceCalled;
@Test
public void minimalConfig() {
@@ -160,6 +167,13 @@ public class HttpOutboundGatewayParserTests {
assertEquals(false, handlerAccessor.getPropertyValue("transferCookies"));
}
@Test
public void withAdvice() {
HttpRequestExecutingMessageHandler handler = (HttpRequestExecutingMessageHandler) new DirectFieldAccessor(
this.withAdvice).getPropertyValue("handler");
handler.handleMessage(new GenericMessage<String>("foo"));
assertEquals(1, adviceCalled);
}
public static class StubErrorHandler implements ResponseErrorHandler {
@@ -171,4 +185,13 @@ public class HttpOutboundGatewayParserTests {
}
}
public static class FooAdvice extends AbstractRequestHandlerAdvice {
@Override
protected Object doInvoke(ExecutionCallback callback, Object target, Message<?> message) throws Exception {
adviceCalled++;
return null;
}
}
}

View File

@@ -84,6 +84,9 @@ message headers (ip_hostName). Default "true".
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="udpAdapterType">
<xsd:all>
<xsd:element name="request-handler-advice-chain" type="integration:adviceChainType" minOccurs="0" maxOccurs="1" />
</xsd:all>
<xsd:attribute name="host" type="xsd:string" />
<xsd:attribute name="acknowledge" type="xsd:string" />
<xsd:attribute name="ack-host" type="xsd:string" />
@@ -176,6 +179,9 @@ task executors such as a WorkManagerTaskExecutor.
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="smartLifeCycleType">
<xsd:all>
<xsd:element name="request-handler-advice-chain" type="integration:adviceChainType" minOccurs="0" maxOccurs="1" />
</xsd:all>
<xsd:attribute name="id" type="xsd:string"/>
<xsd:attribute name="connection-factory" type="xsd:string">
<xsd:annotation>
@@ -298,6 +304,9 @@ task executors such as a WorkManagerTaskExecutor.
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="smartLifeCycleType">
<xsd:all>
<xsd:element name="request-handler-advice-chain" type="integration:adviceChainType" minOccurs="0" maxOccurs="1" />
</xsd:all>
<xsd:attribute name="id" type="xsd:string"/>
<xsd:attribute name="connection-factory" type="xsd:string">
<xsd:annotation>

View File

@@ -138,6 +138,15 @@
order="12"
/>
<int:channel id="udpAdviceChannel" />
<ip:udp-outbound-channel-adapter channel="udpAdviceChannel" host="localhost" port="0">
<ip:request-handler-advice-chain>
<bean class="org.springframework.integration.ip.config.ParserUnitTests$FooAdvice" />
</ip:request-handler-advice-chain>
</ip:udp-outbound-channel-adapter>
<ip:tcp-connection-factory id="cfC1"
type="client"
port="#{tcpIpUtils.findAvailableServerSocket(5700)}"
@@ -154,6 +163,21 @@
phase="125"
/>
<bean id="mockClientCf" class="org.mockito.Mockito" factory-method="mock">
<constructor-arg value="org.springframework.integration.ip.tcp.connection.AbstractClientConnectionFactory" />
</bean>
<int:channel id="tcpAdviceChannel" />
<ip:tcp-outbound-channel-adapter id="tcpOutAdvice"
channel="tcpAdviceChannel"
connection-factory="mockClientCf"
phase="125">
<ip:request-handler-advice-chain>
<bean class="org.springframework.integration.ip.config.ParserUnitTests$FooAdvice" />
</ip:request-handler-advice-chain>
</ip:tcp-outbound-channel-adapter>
<ip:tcp-connection-factory id="cfS2"
type="server"
port="#{tcpIpUtils.findAvailableServerSocket(5800)}"
@@ -199,6 +223,17 @@
phase="127"
/>
<int:channel id="tcpAdviceGateChannel" />
<ip:tcp-outbound-gateway id="outAdviceGateway"
request-channel="tcpAdviceGateChannel"
reply-channel="replyChannel"
connection-factory="mockClientCf">
<ip:request-handler-advice-chain>
<bean class="org.springframework.integration.ip.config.ParserUnitTests$FooAdvice" />
</ip:request-handler-advice-chain>
</ip:tcp-outbound-gateway>
<ip:tcp-connection-factory
id="client1"
type="client"

View File

@@ -36,11 +36,13 @@ import org.springframework.context.ApplicationContext;
import org.springframework.core.serializer.Deserializer;
import org.springframework.core.serializer.Serializer;
import org.springframework.core.task.TaskExecutor;
import org.springframework.integration.Message;
import org.springframework.integration.MessageChannel;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.integration.core.MessageHandler;
import org.springframework.integration.core.MessagingTemplate;
import org.springframework.integration.endpoint.EventDrivenConsumer;
import org.springframework.integration.handler.advice.AbstractRequestHandlerAdvice;
import org.springframework.integration.ip.tcp.TcpInboundGateway;
import org.springframework.integration.ip.tcp.TcpOutboundGateway;
import org.springframework.integration.ip.tcp.TcpReceivingChannelAdapter;
@@ -59,6 +61,7 @@ import org.springframework.integration.ip.udp.MulticastReceivingChannelAdapter;
import org.springframework.integration.ip.udp.MulticastSendingMessageHandler;
import org.springframework.integration.ip.udp.UnicastReceivingChannelAdapter;
import org.springframework.integration.ip.udp.UnicastSendingMessageHandler;
import org.springframework.integration.message.GenericMessage;
import org.springframework.integration.test.util.TestUtils;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.test.context.ContextConfiguration;
@@ -115,6 +118,10 @@ public class ParserUnitTests {
@Qualifier(value="outGateway.handler")
TcpOutboundGateway tcpOutboundGateway;
@Autowired
@Qualifier(value="outAdviceGateway.handler")
TcpOutboundGateway outAdviceGateway;
// verify we can still inject by generated name
@Autowired
@Qualifier(value="org.springframework.integration.ip.tcp.TcpOutboundGateway#0")
@@ -195,6 +202,15 @@ public class ParserUnitTests {
@Autowired
private DirectChannel udpChannel;
@Autowired
private DirectChannel udpAdviceChannel;
@Autowired
private DirectChannel tcpAdviceChannel;
@Autowired
private DirectChannel tcpAdviceGateChannel;
@Autowired
private DirectChannel tcpChannel;
@@ -232,6 +248,8 @@ public class ParserUnitTests {
@Autowired
TcpSocketSupport socketSupport;
private static volatile int adviceCalled;
@Test
public void testInUdp() {
DirectFieldAccessor dfa = new DirectFieldAccessor(udpIn);
@@ -353,6 +371,27 @@ public class ParserUnitTests {
assertSame(this.udpOut, iterator.next());
}
@Test
public void udpAdvice() {
adviceCalled = 0;
this.udpAdviceChannel.send(new GenericMessage<String>("foo"));
assertEquals(1, adviceCalled);
}
@Test
public void tcpAdvice() {
adviceCalled = 0;
this.tcpAdviceChannel.send(new GenericMessage<String>("foo"));
assertEquals(1, adviceCalled);
}
@Test
public void tcpGatewayAdvice() {
adviceCalled = 0;
this.tcpAdviceGateChannel.send(new GenericMessage<String>("foo"));
assertEquals(1, adviceCalled);
}
@Test
public void testOutTcp() {
DirectFieldAccessor dfa = new DirectFieldAccessor(tcpOut);
@@ -592,4 +631,14 @@ public class ParserUnitTests {
assertSame(socketFactorySupport, dfa.getPropertyValue("tcpSocketFactorySupport"));
assertSame(socketSupport, dfa.getPropertyValue("tcpSocketSupport"));
}
public static class FooAdvice extends AbstractRequestHandlerAdvice {
@Override
protected Object doInvoke(ExecutionCallback callback, Object target, Message<?> message) throws Exception {
adviceCalled++;
return null;
}
}
}

View File

@@ -106,6 +106,7 @@ public class JdbcOutboundGateway extends AbstractReplyProducingMessageHandler im
@Override
protected void onInit() {
super.onInit();
if (this.maxRowsPerPoll != null) {
Assert.notNull(poller, "If you want to set 'maxRowsPerPoll', then you must provide a 'selectQuery'.");

View File

@@ -245,9 +245,10 @@
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="queryType">
<xsd:sequence>
<xsd:choice minOccurs="0" maxOccurs="2">
<xsd:element ref="integration:poller" minOccurs="0" maxOccurs="1"/>
</xsd:sequence>
<xsd:element name="request-handler-advice-chain" type="integration:adviceChainType" minOccurs="0" maxOccurs="1" />
</xsd:choice>
<xsd:attribute name="sql-parameter-source-factory" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
@@ -334,7 +335,7 @@
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="selectType">
<xsd:sequence>
<xsd:choice minOccurs="0" maxOccurs="3">
<xsd:element name="update" minOccurs="0" maxOccurs="1">
<xsd:annotation>
<xsd:appinfo>
@@ -357,7 +358,8 @@
</xsd:annotation>
</xsd:element>
<xsd:element ref="integration:poller" minOccurs="0" maxOccurs="1"/>
</xsd:sequence>
<xsd:element name="request-handler-advice-chain" type="integration:adviceChainType" minOccurs="0" maxOccurs="1" />
</xsd:choice>
<xsd:attribute name="id" type="xsd:string" use="optional"/>
<xsd:attribute name="update" type="xsd:string">
<xsd:annotation>
@@ -652,6 +654,7 @@
</xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="request-handler-advice-chain" type="integration:adviceChainType" minOccurs="0" maxOccurs="1" />
</xsd:sequence>
<xsd:attributeGroup ref="coreStoredProcComponentAttributes"/>
<xsd:attribute name="use-payload-as-parameter-source">
@@ -787,6 +790,7 @@
</xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="request-handler-advice-chain" type="integration:adviceChainType" minOccurs="0" maxOccurs="1" />
</xsd:sequence>
</xsd:sequence>
<xsd:attributeGroup ref="coreStoredProcComponentAttributes"/>

View File

@@ -1,11 +1,11 @@
/*
* 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.
@@ -28,6 +28,7 @@ import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.integration.Message;
import org.springframework.integration.MessageChannel;
import org.springframework.integration.handler.advice.AbstractRequestHandlerAdvice;
import org.springframework.integration.jdbc.JdbcMessageHandler;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.integration.test.util.TestUtils;
@@ -45,11 +46,13 @@ import org.springframework.jdbc.core.JdbcTemplate;
public class JdbcMessageHandlerParserTests {
private JdbcTemplate jdbcTemplate;
private MessageChannel channel;
private ConfigurableApplicationContext context;
private static volatile int adviceCalled;
@Test
public void testSimpleOutboundChannelAdapter(){
setUp("handlingWithJdbcOperationsJdbcOutboundChannelAdapterTest.xml", getClass());
@@ -60,6 +63,7 @@ public class JdbcMessageHandlerParserTests {
assertEquals("Wrong id", "foo", map.get("name"));
JdbcMessageHandler handler = context.getBean(JdbcMessageHandler.class);
assertEquals(23, TestUtils.getPropertyValue(handler, "order"));
assertEquals(1, adviceCalled);
}
@Test
@@ -103,7 +107,7 @@ public class JdbcMessageHandlerParserTests {
assertEquals("Wrong id", message.getHeaders().getId().toString(), map.get("ID"));
assertEquals("Wrong name", "bar", map.get("name"));
}
@Test
public void testOutboundAdapterWithPoller() throws Exception{
ApplicationContext ac = new ClassPathXmlApplicationContext("JdbcOutboundAdapterWithPollerTest-context.xml", this.getClass());
@@ -132,11 +136,20 @@ public class JdbcMessageHandlerParserTests {
context.close();
}
}
public void setUp(String name, Class<?> cls){
context = new ClassPathXmlApplicationContext(name, cls);
jdbcTemplate = new JdbcTemplate(this.context.getBean("dataSource",DataSource.class));
channel = this.context.getBean("target", MessageChannel.class);
}
public static class FooAdvice extends AbstractRequestHandlerAdvice {
@Override
protected Object doInvoke(ExecutionCallback callback, Object target, Message<?> message) throws Exception {
adviceCalled++;
return callback.execute();
}
}
}

View File

@@ -32,6 +32,7 @@ import org.springframework.integration.MessageChannel;
import org.springframework.integration.core.MessagingTemplate;
import org.springframework.integration.core.PollableChannel;
import org.springframework.integration.endpoint.PollingConsumer;
import org.springframework.integration.handler.advice.AbstractRequestHandlerAdvice;
import org.springframework.integration.jdbc.JdbcOutboundGateway;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.integration.test.util.TestUtils;
@@ -55,6 +56,8 @@ public class JdbcOutboundGatewayParserTests {
private MessagingTemplate messagingTemplate;
private static volatile int adviceCalled;
@Test
public void testMapPayloadMapReply() {
setUp("handlingMapPayloadJdbcOutboundGatewayTest.xml", getClass());
@@ -71,6 +74,8 @@ public class JdbcOutboundGatewayParserTests {
assertEquals("bar", payload.get("name"));
JdbcOutboundGateway gateway = context.getBean(JdbcOutboundGateway.class);
assertEquals(23, TestUtils.getPropertyValue(gateway, "order"));
Object gw = context.getBean("jdbcGateway");
assertEquals(1, adviceCalled);
}
@Test
@@ -230,4 +235,13 @@ public class JdbcOutboundGatewayParserTests {
setupMessagingTemplate();
}
public static class FooAdvice extends AbstractRequestHandlerAdvice {
@Override
protected Object doInvoke(ExecutionCallback callback, Object target, Message<?> message) throws Exception {
adviceCalled++;
return callback.execute();
}
}
}

View File

@@ -27,14 +27,20 @@ import org.springframework.beans.DirectFieldAccessor;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.expression.Expression;
import org.springframework.integration.Message;
import org.springframework.integration.core.MessageHandler;
import org.springframework.integration.endpoint.EventDrivenConsumer;
import org.springframework.integration.handler.advice.AbstractRequestHandlerAdvice;
import org.springframework.integration.jdbc.storedproc.ProcedureParameter;
import org.springframework.integration.message.GenericMessage;
import org.springframework.integration.test.util.TestUtils;
import org.springframework.jdbc.core.SqlInOutParameter;
import org.springframework.jdbc.core.SqlOutParameter;
import org.springframework.jdbc.core.SqlParameter;
/**
* @author Gunnar Hillert
* @author Gary Russell
* @since 2.1
*
*/
@@ -44,6 +50,8 @@ public class StoredProcMessageHandlerParserTests {
private EventDrivenConsumer consumer;
private static volatile int adviceCalled;
@Test
public void testProcedureNameIsSet() throws Exception {
setUp("basicStoredProcOutboundChannelAdapterTest.xml", getClass());
@@ -149,6 +157,15 @@ public class StoredProcMessageHandlerParserTests {
}
@Test
public void adviceCalled() throws Exception {
setUp("advisedStoredProcOutboundChannelAdapterTest.xml", getClass());
MessageHandler handler = TestUtils.getPropertyValue(this.consumer, "handler", MessageHandler.class);
handler.handleMessage(new GenericMessage<String>("foo"));
assertEquals(1, adviceCalled);
}
@After
public void tearDown(){
if(context != null){
@@ -161,4 +178,13 @@ public class StoredProcMessageHandlerParserTests {
consumer = this.context.getBean("storedProcedureOutboundChannelAdapter", EventDrivenConsumer.class);
}
public static class FooAdvice extends AbstractRequestHandlerAdvice {
@Override
protected Object doInvoke(ExecutionCallback callback, Object target, Message<?> message) throws Exception {
adviceCalled++;
return null;
}
}
}

View File

@@ -30,10 +30,15 @@ import org.springframework.beans.DirectFieldAccessor;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.expression.Expression;
import org.springframework.integration.Message;
import org.springframework.integration.core.MessageHandler;
import org.springframework.integration.core.MessagingTemplate;
import org.springframework.integration.endpoint.EventDrivenConsumer;
import org.springframework.integration.handler.advice.AbstractRequestHandlerAdvice;
import org.springframework.integration.jdbc.storedproc.PrimeMapper;
import org.springframework.integration.jdbc.storedproc.ProcedureParameter;
import org.springframework.integration.message.GenericMessage;
import org.springframework.integration.test.util.TestUtils;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.SqlInOutParameter;
import org.springframework.jdbc.core.SqlOutParameter;
@@ -41,6 +46,7 @@ import org.springframework.jdbc.core.SqlParameter;
/**
* @author Gunnar Hillert
* @author Gary Russell
* @since 2.1
*
*/
@@ -50,6 +56,8 @@ public class StoredProcOutboundGatewayParserTests {
private EventDrivenConsumer outboundGateway;
private static volatile int adviceCalled;
@Test
public void testProcedureNameIsSet() throws Exception {
setUp("storedProcOutboundGatewayParserTest.xml", getClass());
@@ -206,6 +214,15 @@ public class StoredProcOutboundGatewayParserTests {
}
@Test
public void advised() throws Exception {
setUp("advisedStoredProcOutboundGatewayParserTest.xml", getClass());
MessageHandler handler = TestUtils.getPropertyValue(this.outboundGateway, "handler", MessageHandler.class);
handler.handleMessage(new GenericMessage<String>("foo"));
assertEquals(1, adviceCalled);
}
@After
public void tearDown(){
if(context != null){
@@ -218,4 +235,13 @@ public class StoredProcOutboundGatewayParserTests {
this.outboundGateway = this.context.getBean("storedProcedureOutboundGateway", EventDrivenConsumer.class);
}
public static class FooAdvice extends AbstractRequestHandlerAdvice {
@Override
protected Object doInvoke(ExecutionCallback callback, Object target, Message<?> message) throws Exception {
adviceCalled++;
return null;
}
}
}

View File

@@ -0,0 +1,33 @@
<?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:jdbc="http://www.springframework.org/schema/jdbc"
xmlns:int-jdbc="http://www.springframework.org/schema/integration/jdbc"
xmlns:int="http://www.springframework.org/schema/integration"
xsi:schemaLocation="http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/integration/jdbc http://www.springframework.org/schema/integration/jdbc/spring-integration-jdbc.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<int:channel id="target"/>
<jdbc:embedded-database id="dataSource" type="HSQL"/>
<int-jdbc:message-store id="messageStore" data-source="dataSource"/>
<int-jdbc:stored-proc-outbound-channel-adapter id="storedProcedureOutboundChannelAdapter"
data-source="dataSource" channel="target"
stored-procedure-name="testProcedure1">
<int-jdbc:sql-parameter-definition name="username" direction="IN" type="VARCHAR"/>
<int-jdbc:sql-parameter-definition name="password" direction="OUT" />
<int-jdbc:sql-parameter-definition name="age" direction="INOUT" type="INTEGER" scale="5"/>
<int-jdbc:sql-parameter-definition name="description" />
<int-jdbc:parameter name="username" value="kenny" type="java.lang.String"/>
<int-jdbc:parameter name="description" value="Who killed Kenny?"/>
<int-jdbc:parameter name="password" expression="payload.username"/>
<int-jdbc:parameter name="age" value="30" type="java.lang.Integer"/>
<int-jdbc:request-handler-advice-chain>
<bean class="org.springframework.integration.jdbc.config.StoredProcMessageHandlerParserTests$FooAdvice" />
</int-jdbc:request-handler-advice-chain>
</int-jdbc:stored-proc-outbound-channel-adapter>
</beans>

View File

@@ -0,0 +1,39 @@
<?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:jdbc="http://www.springframework.org/schema/jdbc"
xmlns:int-jdbc="http://www.springframework.org/schema/integration/jdbc"
xmlns:int="http://www.springframework.org/schema/integration"
xsi:schemaLocation="http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/integration/jdbc http://www.springframework.org/schema/integration/jdbc/spring-integration-jdbc.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<int:channel id="requestChannel" />
<int:channel id="replyChannel" />
<jdbc:embedded-database id="datasource" type="HSQL" />
<int-jdbc:stored-proc-outbound-gateway
request-channel="requestChannel" stored-procedure-name="GET_PRIME_NUMBERS"
data-source="datasource" auto-startup="true" id="storedProcedureOutboundGateway"
ignore-column-meta-data="false" is-function="false"
skip-undeclared-results="false" order="2" reply-channel="replyChannel"
reply-timeout="555" return-value-required="false">
<int-jdbc:sql-parameter-definition name="username" direction="IN" type="VARCHAR" />
<int-jdbc:sql-parameter-definition name="password" direction="OUT" />
<int-jdbc:sql-parameter-definition name="age" direction="INOUT" type="INTEGER" scale="5" />
<int-jdbc:sql-parameter-definition name="description" />
<int-jdbc:parameter name="username" value="kenny" type="java.lang.String" />
<int-jdbc:parameter name="description" value="Who killed Kenny?" />
<int-jdbc:parameter name="password" expression="payload.username" />
<int-jdbc:parameter name="age" value="30" type="java.lang.Integer" />
<int-jdbc:returning-resultset name="out" row-mapper="org.springframework.integration.jdbc.storedproc.PrimeMapper"/>
<int-jdbc:request-handler-advice-chain>
<bean class="org.springframework.integration.jdbc.config.StoredProcOutboundGatewayParserTests$FooAdvice" />
</int-jdbc:request-handler-advice-chain>
</int-jdbc:stored-proc-outbound-gateway>
<int:poller default="true" fixed-rate="10000" />
</beans>

View File

@@ -17,7 +17,11 @@
</si:channel>
<outbound-gateway id="jdbcGateway" query="select * from foos where id=:headers[id]" update="insert into foos (id, status, name) values (:headers[id], 0, :payload[foo])"
request-channel="target" reply-channel="output" data-source="dataSource" order="23"/>
request-channel="target" reply-channel="output" data-source="dataSource" order="23">
<request-handler-advice-chain>
<beans:bean class="org.springframework.integration.jdbc.config.JdbcOutboundGatewayParserTests$FooAdvice" />
</request-handler-advice-chain>
</outbound-gateway>
<si:chain input-channel="jdbcOutboundGatewayInsideChain" output-channel="replyChannel">
<outbound-gateway query="select * from foos where id=:headers[id]"

View File

@@ -11,7 +11,11 @@
http://www.springframework.org/schema/integration/jdbc/spring-integration-jdbc.xsd">
<outbound-channel-adapter query="insert into foos (id, status, name) values (:headers[business.key], 0, :payload)"
channel="target" jdbc-operations="jdbcTemplate" order="23"/>
channel="target" jdbc-operations="jdbcTemplate" order="23">
<request-handler-advice-chain>
<beans:bean class="org.springframework.integration.jdbc.config.JdbcMessageHandlerParserTests$FooAdvice" />
</request-handler-advice-chain>
</outbound-channel-adapter>
<beans:import resource="jdbcOutboundChannelAdapterCommonConfig.xml" />

View File

@@ -50,7 +50,7 @@ import org.springframework.util.Assert;
/**
* An outbound Messaging Gateway for request/reply JMS.
*
*
* @author Mark Fisher
* @author Arjen Poutsma
* @author Juergen Hoeller
@@ -182,7 +182,7 @@ public class JmsOutboundGateway extends AbstractReplyProducingMessageHandler {
* Specify whether the request destination is a Topic. This value is
* necessary when providing a destination name for a Topic rather than
* a destination reference.
*
*
* @param requestPubSubDomain true if the request destination is a Topic
*/
public void setRequestPubSubDomain(boolean requestPubSubDomain) {
@@ -193,7 +193,7 @@ public class JmsOutboundGateway extends AbstractReplyProducingMessageHandler {
* Specify whether the reply destination is a Topic. This value is
* necessary when providing a destination name for a Topic rather than
* a destination reference.
*
*
* @param replyPubSubDomain true if the reply destination is a Topic
*/
public void setReplyPubSubDomain(boolean replyPubSubDomain) {
@@ -275,8 +275,8 @@ public class JmsOutboundGateway extends AbstractReplyProducingMessageHandler {
}
/**
* This property describes how a JMS Message should be generated from the
* Spring Integration Message. If set to 'true', the body of the JMS Message will be
* This property describes how a JMS Message should be generated from the
* Spring Integration Message. If set to 'true', the body of the JMS Message will be
* created from the Spring Integration Message's payload (via the MessageConverter).
* If set to 'false', then the entire Spring Integration Message will serve as
* the base for JMS Message creation. Since the JMS Message is created by the
@@ -284,7 +284,7 @@ public class JmsOutboundGateway extends AbstractReplyProducingMessageHandler {
* the entire Spring Integration Message or only its payload.
* <br>
* Default is 'true'
*
*
* @param extractRequestPayload
*/
public void setExtractRequestPayload(boolean extractRequestPayload) {
@@ -297,7 +297,7 @@ public class JmsOutboundGateway extends AbstractReplyProducingMessageHandler {
* created from the JMS Reply Message's body (via MessageConverter).
* Otherwise, the entire JMS Message will become the payload of the
* Spring Integration Message.
*
*
* @param extractReplyPayload
*/
public void setExtractReplyPayload(boolean extractReplyPayload) {
@@ -312,6 +312,7 @@ public class JmsOutboundGateway extends AbstractReplyProducingMessageHandler {
this.setOutputChannel(replyChannel);
}
@Override
public String getComponentType() {
return "jms:outbound-gateway";
}
@@ -369,6 +370,7 @@ public class JmsOutboundGateway extends AbstractReplyProducingMessageHandler {
^ this.requestDestinationName != null
^ this.requestDestinationExpressionProcessor != null,
"Exactly one of 'requestDestination', 'requestDestinationName', or 'requestDestinationExpression' is required.");
super.onInit();
if (this.requestDestinationExpressionProcessor != null) {
this.requestDestinationExpressionProcessor.setBeanFactory(getBeanFactory());
this.requestDestinationExpressionProcessor.setConversionService(getConversionService());
@@ -516,7 +518,7 @@ public class JmsOutboundGateway extends AbstractReplyProducingMessageHandler {
"because that ID can only be provided to a MessageSelector after the reuqest Message has been sent thereby " +
"creating a race condition where a fast response might be sent before the MessageConsumer has been created. " +
"Consider providing a value to the 'correlationKey' property of this gateway instead. Then the MessageConsumer " +
"will be created before the request Message is sent.");
"will be created before the request Message is sent.");
}
MessageProducer messageProducer = null;
MessageConsumer messageConsumer = null;
@@ -554,7 +556,7 @@ public class JmsOutboundGateway extends AbstractReplyProducingMessageHandler {
*/
private void deleteDestinationIfTemporary(Destination destination) {
try {
if (destination instanceof TemporaryQueue) {
if (destination instanceof TemporaryQueue) {
((TemporaryQueue) destination).delete();
}
else if (destination instanceof TemporaryTopic) {

View File

@@ -731,9 +731,10 @@
</xsd:documentation>
</xsd:annotation>
<xsd:complexType>
<xsd:sequence>
<xsd:choice minOccurs="0" maxOccurs="2">
<xsd:element ref="integration:poller" minOccurs="0" maxOccurs="1"/>
</xsd:sequence>
<xsd:element name="request-handler-advice-chain" type="integration:adviceChainType" minOccurs="0" maxOccurs="1" />
</xsd:choice>
<xsd:attribute name="id" type="xsd:string"/>
<xsd:attribute name="request-channel" type="xsd:string">
<xsd:annotation>
@@ -967,9 +968,10 @@
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="jmsAdapterType">
<xsd:sequence>
<xsd:choice minOccurs="0" maxOccurs="2">
<xsd:element ref="integration:poller" minOccurs="0" maxOccurs="1"/>
</xsd:sequence>
<xsd:element name="request-handler-advice-chain" type="integration:adviceChainType" minOccurs="0" maxOccurs="1" />
</xsd:choice>
<xsd:attribute name="destination" type="xsd:string">
<xsd:annotation>
<xsd:documentation>

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.
@@ -23,20 +23,27 @@ import static org.junit.Assert.assertTrue;
import javax.jms.DeliveryMode;
import org.junit.Test;
import org.springframework.beans.DirectFieldAccessor;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.integration.Message;
import org.springframework.integration.core.MessageHandler;
import org.springframework.integration.endpoint.EventDrivenConsumer;
import org.springframework.integration.handler.advice.AbstractRequestHandlerAdvice;
import org.springframework.integration.jms.JmsHeaderMapper;
import org.springframework.integration.message.GenericMessage;
import org.springframework.integration.test.util.TestUtils;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.support.converter.MessageConverter;
/**
* @author Mark Fisher
* @author Gary Russell
*/
public class JmsOutboundChannelAdapterParserTests {
private static volatile int adviceCalled;
@Test
public void adapterWithConnectionFactoryAndDestination() {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
@@ -47,6 +54,16 @@ public class JmsOutboundChannelAdapterParserTests {
assertNotNull(accessor.getPropertyValue("jmsTemplate"));
}
@Test
public void advisedAdapter() {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
"jmsOutboundWithConnectionFactoryAndDestination.xml", this.getClass());
EventDrivenConsumer endpoint = (EventDrivenConsumer) context.getBean("advised");
MessageHandler handler = TestUtils.getPropertyValue(endpoint, "handler", MessageHandler.class);
handler.handleMessage(new GenericMessage<String>("foo"));
assertEquals(1, adviceCalled);
}
@Test
public void adapterWithConnectionFactoryAndDestinationName() {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
@@ -164,4 +181,13 @@ public class JmsOutboundChannelAdapterParserTests {
assertEquals(false, accessor.getPropertyValue("explicitQosEnabled"));
}
public static class FooAdvice extends AbstractRequestHandlerAdvice {
@Override
protected Object doInvoke(ExecutionCallback callback, Object target, Message<?> message) throws Exception {
adviceCalled++;
return null;
}
}
}

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.
@@ -30,7 +30,6 @@ import javax.jms.DeliveryMode;
import org.junit.Test;
import org.mockito.Mockito;
import org.springframework.beans.DirectFieldAccessor;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.integration.Message;
@@ -41,9 +40,11 @@ import org.springframework.integration.core.MessagingTemplate;
import org.springframework.integration.core.SubscribableChannel;
import org.springframework.integration.endpoint.EventDrivenConsumer;
import org.springframework.integration.endpoint.PollingConsumer;
import org.springframework.integration.handler.advice.AbstractRequestHandlerAdvice;
import org.springframework.integration.history.MessageHistory;
import org.springframework.integration.jms.JmsOutboundGateway;
import org.springframework.integration.jms.StubMessageConverter;
import org.springframework.integration.message.GenericMessage;
import org.springframework.integration.test.util.TestUtils;
import org.springframework.jms.support.converter.MessageConverter;
@@ -51,9 +52,12 @@ import org.springframework.jms.support.converter.MessageConverter;
* @author Jonas Partner
* @author Oleg Zhurakousky
* @author Mark Fisher
* @author Gary Russell
*/
public class JmsOutboundGatewayParserTests {
private static volatile int adviceCalled;
@Test
public void testWithDeliveryPersistentAttribute(){
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
@@ -66,6 +70,16 @@ public class JmsOutboundGatewayParserTests {
assertEquals(DeliveryMode.PERSISTENT, deliveryMode);
}
@Test
public void testAdvised(){
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
"jmsOutboundGatewayWithDeliveryPersistent.xml", this.getClass());
EventDrivenConsumer endpoint = (EventDrivenConsumer) context.getBean("advised");
JmsOutboundGateway gateway = TestUtils.getPropertyValue(endpoint, "handler", JmsOutboundGateway.class);
gateway.handleMessage(new GenericMessage<String>("foo"));
assertEquals(1, adviceCalled);
}
@Test
public void testDefault(){
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
@@ -88,7 +102,7 @@ public class JmsOutboundGatewayParserTests {
Object order = accessor.getPropertyValue("order");
assertEquals(99, order);
}
@Test
public void gatewayMaintainsReplyChannelAndInboundHistory() {
ActiveMqTestUtils.prepare();
@@ -96,7 +110,7 @@ public class JmsOutboundGatewayParserTests {
"gatewayMaintainsReplyChannel.xml", this.getClass());
SampleGateway gateway = context.getBean("gateway", SampleGateway.class);
SubscribableChannel jmsInput = context.getBean("jmsInput", SubscribableChannel.class);
MessageHandler handler = new MessageHandler() {
MessageHandler handler = new MessageHandler() {
public void handleMessage(Message<?> message) throws MessagingException {
MessageHistory history = MessageHistory.read(message);
assertNotNull(history);
@@ -139,11 +153,20 @@ public class JmsOutboundGatewayParserTests {
public static interface SampleGateway{
public String echo(String value);
}
public static class SampleService{
public String echo(String value){
return value.toUpperCase();
}
}
public static class FooAdvice extends AbstractRequestHandlerAdvice {
@Override
protected Object doInvoke(ExecutionCallback callback, Object target, Message<?> message) throws Exception {
adviceCalled++;
return null;
}
}
}

View File

@@ -24,5 +24,14 @@
</bean>
</constructor-arg>
</bean>
<jms:outbound-gateway id="advised"
request-destination-name="requestQueue"
request-channel="requestChannel"
delivery-persistent="true">
<jms:request-handler-advice-chain>
<bean class="org.springframework.integration.jms.config.JmsOutboundGatewayParserTests$FooAdvice" />
</jms:request-handler-advice-chain>
</jms:outbound-gateway>
</beans>

View File

@@ -27,4 +27,13 @@
<bean id="testDestination" class="org.springframework.integration.jms.StubDestination"/>
<jms:outbound-channel-adapter id="advised"
channel="input"
connection-factory="testConnectionFactory"
destination="testDestination">
<jms:request-handler-advice-chain>
<bean class="org.springframework.integration.jms.config.JmsOutboundChannelAdapterParserTests$FooAdvice" />
</jms:request-handler-advice-chain>
</jms:outbound-channel-adapter>
</beans>

View File

@@ -103,6 +103,7 @@ public class OperationInvokingMessageHandler extends AbstractReplyProducingMessa
@Override
public final void onInit() {
Assert.notNull(this.server, "MBeanServer is required.");
super.onInit();
}
@Override

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.
@@ -16,16 +16,17 @@
package org.springframework.integration.jmx.config;
import org.w3c.dom.Element;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.integration.config.xml.AbstractOutboundChannelAdapterParser;
import org.springframework.integration.config.xml.IntegrationNamespaceUtils;
import org.springframework.integration.jmx.NotificationPublishingMessageHandler;
import org.w3c.dom.Element;
/**
* @author Mark Fisher
* @author Gary Russell
* @since 2.0
*/
public class NotificationPublishingChannelAdapterParser extends AbstractOutboundChannelAdapterParser {
@@ -37,8 +38,8 @@ public class NotificationPublishingChannelAdapterParser extends AbstractOutbound
@Override
protected AbstractBeanDefinition parseConsumer(Element element, ParserContext parserContext) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(
"org.springframework.integration.jmx.NotificationPublishingMessageHandler");
BeanDefinitionBuilder builder = BeanDefinitionBuilder
.rootBeanDefinition(NotificationPublishingMessageHandler.class);
builder.addConstructorArgValue(element.getAttribute("object-name"));
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "default-notification-type");
return builder.getBeanDefinition();

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.
@@ -16,16 +16,17 @@
package org.springframework.integration.jmx.config;
import org.w3c.dom.Element;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.integration.config.xml.AbstractOutboundChannelAdapterParser;
import org.springframework.integration.config.xml.IntegrationNamespaceUtils;
import org.springframework.integration.jmx.OperationInvokingMessageHandler;
import org.w3c.dom.Element;
/**
* @author Mark Fisher
* @author Gary Russell
* @since 2.0
*/
public class OperationInvokingChannelAdapterParser extends AbstractOutboundChannelAdapterParser {
@@ -37,8 +38,7 @@ public class OperationInvokingChannelAdapterParser extends AbstractOutboundChann
@Override
protected AbstractBeanDefinition parseConsumer(Element element, ParserContext parserContext) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(
"org.springframework.integration.jmx.OperationInvokingMessageHandler");
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(OperationInvokingMessageHandler.class);
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "server");
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "object-name");
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "operation-name");

View File

@@ -42,6 +42,9 @@
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="operationInvokingType">
<xsd:all>
<xsd:element name="request-handler-advice-chain" type="integration:adviceChainType" minOccurs="0" maxOccurs="1" />
</xsd:all>
<xsd:attribute name="request-channel" type="xsd:string" />
<xsd:attribute name="reply-channel" type="xsd:string" use="optional" />
</xsd:extension>
@@ -58,6 +61,9 @@
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="operationInvokingType">
<xsd:all>
<xsd:element name="request-handler-advice-chain" type="integration:adviceChainType" minOccurs="0" maxOccurs="1" />
</xsd:all>
<xsd:attribute name="channel" type="xsd:string" use="optional" />
</xsd:extension>
</xsd:complexContent>
@@ -90,6 +96,9 @@
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="adapterType">
<xsd:all>
<xsd:element name="request-handler-advice-chain" type="integration:adviceChainType" minOccurs="0" maxOccurs="1" />
</xsd:all>
<xsd:attribute name="default-notification-type" type="xsd:string" use="optional" />
</xsd:extension>
</xsd:complexContent>

View File

@@ -22,7 +22,11 @@
<jmx:notification-publishing-channel-adapter
id="adapter" channel="channel"
object-name="test.publisher:name=publisher"
default-notification-type="default.type"/>
default-notification-type="default.type">
<jmx:request-handler-advice-chain>
<bean class="org.springframework.integration.jmx.config.NotificationPublishingChannelAdapterParserTests$FooADvice" />
</jmx:request-handler-advice-chain>
</jmx:notification-publishing-channel-adapter>
<si:chain input-channel="publishingWithinChainChannel">

View File

@@ -28,6 +28,7 @@ import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.integration.Message;
import org.springframework.integration.MessageChannel;
import org.springframework.integration.handler.advice.AbstractRequestHandlerAdvice;
import org.springframework.integration.jmx.JmxHeaders;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.test.context.ContextConfiguration;
@@ -36,6 +37,7 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @author Mark Fisher
* @author Artem Bilan
* @author Gary Russell
* @since 2.0
*/
@ContextConfiguration
@@ -51,6 +53,8 @@ public class NotificationPublishingChannelAdapterParserTests {
@Autowired
private MessageChannel publishingWithinChainChannel;
private static volatile int adviceCalled;
@After
public void clearListener() {
listener.lastNotification = null;
@@ -59,6 +63,7 @@ public class NotificationPublishingChannelAdapterParserTests {
@Test
public void publishStringMessage() throws Exception {
adviceCalled = 0;
assertNull(listener.lastNotification);
Message<?> message = MessageBuilder.withPayload("XYZ")
.setHeader(JmxHeaders.NOTIFICATION_TYPE, "test.type").build();
@@ -68,6 +73,7 @@ public class NotificationPublishingChannelAdapterParserTests {
assertEquals("XYZ", notification.getMessage());
assertEquals("test.type", notification.getType());
assertNull(notification.getUserData());
assertEquals(1, adviceCalled);
}
@Test
@@ -110,4 +116,15 @@ public class NotificationPublishingChannelAdapterParserTests {
private static class TestData {
}
public static class FooADvice extends AbstractRequestHandlerAdvice {
@Override
protected Object doInvoke(ExecutionCallback callback, Object target, Message<?> message) throws Exception {
adviceCalled++;
System.out.println("foo");
new RuntimeException("foo").printStackTrace();
return callback.execute();
}
}
}

View File

@@ -18,7 +18,11 @@
<jmx:operation-invoking-channel-adapter id="input"
object-name="org.springframework.integration.jmx.config:type=TestBean,name=testBeanAdapter"
operation-name="test"/>
operation-name="test">
<jmx:request-handler-advice-chain>
<bean class="org.springframework.integration.jmx.config.OperationInvokingChannelAdapterParserTests$FooADvice" />
</jmx:request-handler-advice-chain>
</jmx:operation-invoking-channel-adapter>
<jmx:operation-invoking-channel-adapter id="operationWithNonNullReturn"
object-name="org.springframework.integration.jmx.config:type=TestBean,name=testBeanAdapter"

View File

@@ -27,6 +27,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.integration.Message;
import org.springframework.integration.MessageChannel;
import org.springframework.integration.MessagingException;
import org.springframework.integration.handler.advice.AbstractRequestHandlerAdvice;
import org.springframework.integration.jmx.JmxHeaders;
import org.springframework.integration.message.GenericMessage;
import org.springframework.integration.support.MessageBuilder;
@@ -58,6 +59,8 @@ public class OperationInvokingChannelAdapterParserTests {
@Autowired
private TestBean testBean;
private static volatile int adviceCalled;
@After
public void resetLists() {
testBean.messages.clear();
@@ -71,6 +74,7 @@ public class OperationInvokingChannelAdapterParserTests {
input.send(new GenericMessage<String>("test2"));
input.send(new GenericMessage<String>("test3"));
assertEquals(3, testBean.messages.size());
assertEquals(3, adviceCalled);
}
@Test
@@ -118,4 +122,14 @@ public class OperationInvokingChannelAdapterParserTests {
.setHeader(JmxHeaders.OBJECT_NAME, "org.springframework.integration.jmx.config:type=TestBean,name=foo")
.setHeader(JmxHeaders.OPERATION_NAME, "blah").build();
}
public static class FooADvice extends AbstractRequestHandlerAdvice {
@Override
protected Object doInvoke(ExecutionCallback callback, Object target, Message<?> message) throws Exception {
adviceCalled++;
return callback.execute();
}
}
}

View File

@@ -27,7 +27,11 @@
<jmx:operation-invoking-outbound-gateway request-channel="withReplyChannel"
reply-channel="withReplyChannelOutput"
object-name="org.springframework.integration.jmx.config:type=TestBean,name=testBeanGateway"
operation-name="testWithReturn"/>
operation-name="testWithReturn">
<jmx:request-handler-advice-chain>
<bean class="org.springframework.integration.jmx.config.OperationInvokingOutboundGatewayTests$FooADvice" />
</jmx:request-handler-advice-chain>
</jmx:operation-invoking-outbound-gateway>
<si:chain input-channel="jmxOutboundGatewayInsideChain" output-channel="withReplyChannelOutput">
<jmx:operation-invoking-outbound-gateway operation-name="testWithReturn"

View File

@@ -25,8 +25,10 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.integration.Message;
import org.springframework.integration.MessageChannel;
import org.springframework.integration.core.PollableChannel;
import org.springframework.integration.handler.advice.AbstractRequestHandlerAdvice;
import org.springframework.integration.message.GenericMessage;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.test.context.ContextConfiguration;
@@ -59,6 +61,8 @@ public class OperationInvokingOutboundGatewayTests {
@Autowired
private TestBean testBean;
private static volatile int adviceCalled;
@After
public void resetLists() {
testBean.messages.clear();
@@ -72,6 +76,7 @@ public class OperationInvokingOutboundGatewayTests {
assertEquals(2, ((List<?>) withReplyChannelOutput.receive().getPayload()).size());
withReplyChannel.send(new GenericMessage<String>("3"));
assertEquals(3, ((List<?>) withReplyChannelOutput.receive().getPayload()).size());
assertEquals(3, adviceCalled);
}
@Test
@@ -94,4 +99,13 @@ public class OperationInvokingOutboundGatewayTests {
assertEquals(3, ((List<?>) withReplyChannelOutput.receive().getPayload()).size());
}
public static class FooADvice extends AbstractRequestHandlerAdvice {
@Override
protected Object doInvoke(ExecutionCallback callback, Object target, Message<?> message) throws Exception {
adviceCalled++;
return callback.execute();
}
}
}

View File

@@ -73,11 +73,16 @@ public class JpaOutboundChannelAdapterParser extends AbstractOutboundChannelAdap
BeanDefinition txAdviceDefinition = IntegrationNamespaceUtils.configureTransactionAttributes(transactionalElement);
ManagedList<BeanDefinition> adviceChain = new ManagedList<BeanDefinition>();
adviceChain.add(txAdviceDefinition);
jpaOutboundChannelAdapterBuilder.addPropertyValue("adviceChain", adviceChain);
jpaOutboundChannelAdapterBuilder.addPropertyValue("txAdviceChain", adviceChain);
}
return jpaOutboundChannelAdapterBuilder.getBeanDefinition();
}
@Override
protected boolean isUsingReplyProducer() {
return true;
}
}

View File

@@ -39,19 +39,34 @@ import org.springframework.util.CollectionUtils;
*
* @author Amol Nayak
* @author Gunnar Hillert
* @author Gary Russell
* @since 2.2
*
*/
public class JpaOutboundGatewayFactoryBean extends AbstractFactoryBean<MessageHandler> {
private final JpaExecutor jpaExecutor;
private final JpaExecutor jpaExecutor;
private OutboundGatewayType gatewayType = OutboundGatewayType.UPDATING;
/**
* &lt;transactional /&gt; element applies to entire flow from this point
*/
private volatile List<Advice> txAdviceChain;
/**
* &lt;request-handler-advice-chain /&gt; only applies to the handleRequestMessage.
*/
private volatile List<Advice> adviceChain;
private volatile ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();
private boolean producesReply = true;
private MessageChannel outputChannel;
private int order;
private long replyTimeout;
/**
@@ -69,34 +84,8 @@ public class JpaOutboundGatewayFactoryBean extends AbstractFactoryBean<MessageHa
this.gatewayType = gatewayType;
}
@Override
public Class<?> getObjectType() {
return MessageHandler.class;
}
@Override
protected MessageHandler createInstance() {
JpaOutboundGateway jpaOutboundGateway = new JpaOutboundGateway(jpaExecutor);
jpaOutboundGateway.setGatewayType(this.gatewayType);
jpaOutboundGateway.setProducesReply(this.producesReply);
jpaOutboundGateway.setOutputChannel(this.outputChannel);
jpaOutboundGateway.setOrder(this.order);
jpaOutboundGateway.setSendTimeout(replyTimeout);
if (!CollectionUtils.isEmpty(this.adviceChain)) {
ProxyFactory proxyFactory = new ProxyFactory(jpaOutboundGateway);
if (!CollectionUtils.isEmpty(adviceChain)) {
for (Advice advice : adviceChain) {
proxyFactory.addAdvice(advice);
}
}
return (MessageHandler) proxyFactory.getProxy(this.beanClassLoader);
}
return jpaOutboundGateway;
public void setTxAdviceChain(List<Advice> txAdviceChain) {
this.txAdviceChain = txAdviceChain;
}
public void setAdviceChain(List<Advice> adviceChain) {
@@ -125,7 +114,38 @@ public class JpaOutboundGatewayFactoryBean extends AbstractFactoryBean<MessageHa
public void setReplyTimeout(long replyTimeout) {
this.replyTimeout = replyTimeout;
}
@Override
public Class<?> getObjectType() {
return MessageHandler.class;
}
@Override
protected MessageHandler createInstance() {
JpaOutboundGateway jpaOutboundGateway = new JpaOutboundGateway(jpaExecutor);
jpaOutboundGateway.setGatewayType(this.gatewayType);
jpaOutboundGateway.setProducesReply(this.producesReply);
jpaOutboundGateway.setOutputChannel(this.outputChannel);
jpaOutboundGateway.setOrder(this.order);
jpaOutboundGateway.setSendTimeout(replyTimeout);
if (this.adviceChain != null) {
jpaOutboundGateway.setAdviceChain(this.adviceChain);
}
jpaOutboundGateway.afterPropertiesSet();
if (!CollectionUtils.isEmpty(this.txAdviceChain)) {
ProxyFactory proxyFactory = new ProxyFactory(jpaOutboundGateway);
if (!CollectionUtils.isEmpty(txAdviceChain)) {
for (Advice advice : txAdviceChain) {
proxyFactory.addAdvice(advice);
}
}
return (MessageHandler) proxyFactory.getProxy(this.beanClassLoader);
}
return jpaOutboundGateway;
}
}

View File

@@ -9,7 +9,7 @@
<xsd:import namespace="http://www.springframework.org/schema/beans" />
<xsd:import namespace="http://www.springframework.org/schema/tool" />
<xsd:import namespace="http://www.springframework.org/schema/integration"
schemaLocation="http://www.springframework.org/schema/integration/spring-integration-2.1.xsd" />
schemaLocation="http://www.springframework.org/schema/integration/spring-integration-2.2.xsd" />
<xsd:annotation>
<xsd:documentation><![CDATA[
@@ -96,6 +96,7 @@
</xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="request-handler-advice-chain" type="integration:adviceChainType" minOccurs="0" maxOccurs="1" />
</xsd:sequence>
<xsd:attributeGroup ref="coreJpaComponentAttributes"/>
<xsd:attributeGroup ref="commonUpdatingJpaAttributes"/>
@@ -187,6 +188,7 @@
</xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="request-handler-advice-chain" type="integration:adviceChainType" minOccurs="0" maxOccurs="1" />
</xsd:sequence>
<xsd:attributeGroup ref="coreJpaComponentAttributes" />
<xsd:attributeGroup ref="commonUpdatingJpaAttributes"/>
@@ -218,6 +220,7 @@
</xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="request-handler-advice-chain" type="integration:adviceChainType" minOccurs="0" maxOccurs="1" />
</xsd:sequence>
<xsd:attributeGroup ref="coreJpaComponentAttributes" />
<xsd:attributeGroup ref="commonJpaOutboundGatewayAttributes"/>

View File

@@ -24,12 +24,16 @@ import org.junit.After;
import org.junit.Test;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.integration.Message;
import org.springframework.integration.channel.AbstractMessageChannel;
import org.springframework.integration.core.MessageHandler;
import org.springframework.integration.endpoint.EventDrivenConsumer;
import org.springframework.integration.handler.advice.AbstractRequestHandlerAdvice;
import org.springframework.integration.jpa.core.JpaExecutor;
import org.springframework.integration.jpa.core.JpaOperations;
import org.springframework.integration.jpa.support.JpaParameter;
import org.springframework.integration.jpa.support.PersistMode;
import org.springframework.integration.message.GenericMessage;
import org.springframework.integration.test.util.TestUtils;
/**
@@ -44,6 +48,8 @@ public class JpaMessageHandlerParserTests {
private EventDrivenConsumer consumer;
private static volatile int adviceCalled;
@Test
public void testJpaMessageHandlerParser() throws Exception {
setUp("JpaMessageHandlerParserTests.xml", getClass());
@@ -77,6 +83,47 @@ public class JpaMessageHandlerParserTests {
}
@Test
public void advised() throws Exception {
setUp("JpaMessageHandlerParserTests.xml", getClass());
EventDrivenConsumer consumer = this.context.getBean("advised", EventDrivenConsumer.class);
final AbstractMessageChannel inputChannel = TestUtils.getPropertyValue(consumer, "inputChannel", AbstractMessageChannel.class);
assertEquals("target", inputChannel.getComponentName());
final MessageHandler handler = TestUtils.getPropertyValue(consumer, "handler", MessageHandler.class);
adviceCalled = 0;
handler.handleMessage(new GenericMessage<String>("foo"));
assertEquals(1, adviceCalled);
}
/*
* Tests that an already advised handler (tx) gets the request handler advice added to its chain.
*/
@Test
public void advisedAndTransactional() throws Exception {
setUp("JpaMessageHandlerParserTests.xml", getClass());
EventDrivenConsumer consumer = this.context.getBean("advisedAndTransactional", EventDrivenConsumer.class);
final AbstractMessageChannel inputChannel = TestUtils.getPropertyValue(consumer, "inputChannel", AbstractMessageChannel.class);
assertEquals("target", inputChannel.getComponentName());
final MessageHandler handler = TestUtils.getPropertyValue(consumer, "handler", MessageHandler.class);
adviceCalled = 0;
handler.handleMessage(new GenericMessage<String>("foo"));
assertEquals(1, adviceCalled);
}
@Test
public void testJpaMessageHandlerParserWithEntityManagerFactory() throws Exception {
setUp("JpaMessageHandlerParserTestsWithEmFactory.xml", getClass());
@@ -156,7 +203,7 @@ public class JpaMessageHandlerParserTests {
assertNotNull(context.getBean("jpaOutboundChannelAdapter.jpaExecutor", JpaExecutor.class));
}
@After
public void tearDown(){
if(context != null){
@@ -169,4 +216,13 @@ public class JpaMessageHandlerParserTests {
consumer = this.context.getBean("jpaOutboundChannelAdapter", EventDrivenConsumer.class);
}
public static class FooAdvice extends AbstractRequestHandlerAdvice {
@Override
protected Object doInvoke(ExecutionCallback callback, Object target, Message<?> message) throws Exception {
adviceCalled++;
return null;
}
}
}

View File

@@ -26,4 +26,37 @@
<int-jpa:parameter name="updatedDateTime" expression="new java.util.Date()"/>
</int-jpa:outbound-channel-adapter>
<int-jpa:outbound-channel-adapter id="advised"
entity-manager="entityManager"
auto-startup="true"
entity-class="org.springframework.integration.jpa.test.entity.StudentDomain"
jpa-query="from Student"
persist-mode="PERSIST"
order="1"
channel="target">
<int-jpa:parameter name="firstName" value="kenny" type="java.lang.String"/>
<int-jpa:parameter name="firstaName" value="cartman"/>
<int-jpa:parameter name="updatedDateTime" expression="new java.util.Date()"/>
<int-jpa:request-handler-advice-chain>
<bean class="org.springframework.integration.jpa.config.xml.JpaMessageHandlerParserTests$FooAdvice" />
</int-jpa:request-handler-advice-chain>
</int-jpa:outbound-channel-adapter>
<int-jpa:outbound-channel-adapter id="advisedAndTransactional"
entity-manager="entityManager"
auto-startup="true"
entity-class="org.springframework.integration.jpa.test.entity.StudentDomain"
jpa-query="from Student"
persist-mode="PERSIST"
order="1"
channel="target">
<int-jpa:transactional />
<int-jpa:parameter name="firstName" value="kenny" type="java.lang.String"/>
<int-jpa:parameter name="firstaName" value="cartman"/>
<int-jpa:parameter name="updatedDateTime" expression="new java.util.Date()"/>
<int-jpa:request-handler-advice-chain>
<bean class="org.springframework.integration.jpa.config.xml.JpaMessageHandlerParserTests$FooAdvice" />
</int-jpa:request-handler-advice-chain>
</int-jpa:outbound-channel-adapter>
</beans>

View File

@@ -20,18 +20,22 @@ import org.junit.After;
import org.junit.Test;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.integration.Message;
import org.springframework.integration.channel.AbstractMessageChannel;
import org.springframework.integration.endpoint.EventDrivenConsumer;
import org.springframework.integration.handler.advice.AbstractRequestHandlerAdvice;
import org.springframework.integration.jpa.core.JpaExecutor;
import org.springframework.integration.jpa.core.JpaOperations;
import org.springframework.integration.jpa.outbound.JpaOutboundGateway;
import org.springframework.integration.jpa.support.OutboundGatewayType;
import org.springframework.integration.jpa.support.PersistMode;
import org.springframework.integration.message.GenericMessage;
import org.springframework.integration.test.util.TestUtils;
/**
* @author Gunnar Hillert
* @author Amol Nayak
* @author Gary Russell
* @since 2.2
*
*/
@@ -41,6 +45,8 @@ public class JpaOutboundGatewayParserTests {
private EventDrivenConsumer consumer;
private static volatile int adviceCalled;
@Test
public void testRetrievingJpaOutboundGatewayParser() throws Exception {
setUp("JpaOutboundGatewayParserTests.xml", getClass(), "retrievingJpaOutboundGateway");
@@ -126,6 +132,16 @@ public class JpaOutboundGatewayParserTests {
}
@Test
public void advised() throws Exception {
setUp("JpaOutboundGatewayParserTests.xml", getClass(), "updatingJpaOutboundGateway");
EventDrivenConsumer consumer = this.context.getBean("advised", EventDrivenConsumer.class);
final JpaOutboundGateway jpaOutboundGateway = TestUtils.getPropertyValue(consumer, "handler", JpaOutboundGateway.class);
jpaOutboundGateway.handleMessage(new GenericMessage<String>("foo"));
assertEquals(1, adviceCalled);
}
@Test
public void testJpaExecutorBeanIdNaming() throws Exception {
@@ -148,4 +164,13 @@ public class JpaOutboundGatewayParserTests {
consumer = this.context.getBean(gatewayId, EventDrivenConsumer.class);
}
public static class FooAdvice extends AbstractRequestHandlerAdvice {
@Override
protected Object doInvoke(ExecutionCallback callback, Object target, Message<?> message) throws Exception {
adviceCalled++;
return null;
}
}
}

View File

@@ -36,4 +36,19 @@
reply-channel="out"
reply-timeout="100"/>
<int-jpa:updating-outbound-gateway id="advised"
entity-manager-factory="entityManagerFactory"
auto-startup="false"
entity-class="org.springframework.integration.jpa.test.entity.StudentDomain"
persist-mode="PERSIST"
use-payload-as-parameter-source="true"
order="2"
request-channel="in"
reply-channel="out"
reply-timeout="100">
<int-jpa:request-handler-advice-chain>
<bean class="org.springframework.integration.jpa.config.xml.JpaOutboundGatewayParserTests$FooAdvice" />
</int-jpa:request-handler-advice-chain>
</int-jpa:updating-outbound-gateway>
</beans>

View File

@@ -26,9 +26,10 @@
Defines an outbound mail-sending Channel Adapter.
</xsd:documentation>
</xsd:annotation>
<xsd:sequence>
<xsd:choice minOccurs="0" maxOccurs="2">
<xsd:element ref="integration:poller" minOccurs="0" maxOccurs="1"/>
</xsd:sequence>
<xsd:element name="request-handler-advice-chain" type="integration:adviceChainType" minOccurs="0" maxOccurs="1" />
</xsd:choice>
<xsd:attribute name="id" type="xsd:string"/>
<xsd:attribute name="channel" type="xsd:string">
<xsd:annotation>

View File

@@ -16,27 +16,33 @@
package org.springframework.integration.mail.config;
import static junit.framework.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import java.util.Properties;
import org.junit.Test;
import org.springframework.beans.DirectFieldAccessor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.integration.Message;
import org.springframework.integration.channel.QueueChannel;
import org.springframework.integration.core.MessageHandler;
import org.springframework.integration.endpoint.PollingConsumer;
import org.springframework.integration.handler.advice.AbstractRequestHandlerAdvice;
import org.springframework.integration.mail.MailSendingMessageHandler;
import org.springframework.integration.message.GenericMessage;
import org.springframework.integration.test.util.TestUtils;
import org.springframework.mail.MailSender;
import static junit.framework.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
/**
* @author Mark Fisher
* @author Gary Russell
*/
public class MailOutboundChannelAdapterParserTests {
public static volatile int adviceCalled;
@Test
public void adapterWithMailSenderReference() {
ApplicationContext context = new ClassPathXmlApplicationContext(
@@ -50,6 +56,17 @@ public class MailOutboundChannelAdapterParserTests {
assertEquals(23, fieldAccessor.getPropertyValue("order"));
}
@Test
public void advised() {
ApplicationContext context = new ClassPathXmlApplicationContext(
"mailOutboundChannelAdapterParserTests.xml", this.getClass());
Object adapter = context.getBean("advised.adapter");
MessageHandler handler = (MessageHandler)
new DirectFieldAccessor(adapter).getPropertyValue("handler");
handler.handleMessage(new GenericMessage<String>("foo"));
assertEquals(1, adviceCalled);
}
@Test
public void adapterWithHostProperty() {
ApplicationContext context = new ClassPathXmlApplicationContext(
@@ -61,7 +78,7 @@ public class MailOutboundChannelAdapterParserTests {
MailSender mailSender = (MailSender) fieldAccessor.getPropertyValue("mailSender");
assertNotNull(mailSender);
}
@Test
public void adapterWithPollableChannel() {
ApplicationContext context = new ClassPathXmlApplicationContext(
@@ -70,7 +87,7 @@ public class MailOutboundChannelAdapterParserTests {
QueueChannel pollableChannel = TestUtils.getPropertyValue(pc, "inputChannel", QueueChannel.class);
assertEquals("pollableChannel", pollableChannel.getComponentName());
}
@Test
public void adapterWithJavaMailProperties() {
ApplicationContext context = new ClassPathXmlApplicationContext(
@@ -87,4 +104,13 @@ public class MailOutboundChannelAdapterParserTests {
assertEquals("true", javaMailProperties.get("mail.smtps.auth"));
}
public static class FooAdvice extends AbstractRequestHandlerAdvice {
@Override
protected Object doInvoke(ExecutionCallback callback, Object target, Message<?> message) throws Exception {
adviceCalled++;
return null;
}
}
}

View File

@@ -32,4 +32,11 @@
</constructor-arg>
</bean>
<int-mail:outbound-channel-adapter id="advised"
mail-sender="mailSender">
<int-mail:request-handler-advice-chain>
<bean class="org.springframework.integration.mail.config.MailOutboundChannelAdapterParserTests$FooAdvice" />
</int-mail:request-handler-advice-chain>
</int-mail:outbound-channel-adapter>
</beans>

View File

@@ -63,9 +63,10 @@
</xsd:annotation>
<xsd:complexContent>
<xsd:extension base="gatewayType">
<xsd:sequence>
<xsd:choice minOccurs="0" maxOccurs="2">
<xsd:element ref="integration:poller" minOccurs="0" maxOccurs="1"/>
</xsd:sequence>
<xsd:element name="request-handler-advice-chain" type="integration:adviceChainType" minOccurs="0" maxOccurs="1" />
</xsd:choice>
<xsd:attribute name="request-channel" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>

View File

@@ -27,6 +27,7 @@ import org.springframework.integration.Message;
import org.springframework.integration.MessageChannel;
import org.springframework.integration.channel.QueueChannel;
import org.springframework.integration.core.PollableChannel;
import org.springframework.integration.handler.advice.AbstractRequestHandlerAdvice;
import org.springframework.integration.message.GenericMessage;
import org.springframework.integration.rmi.RmiInboundGateway;
import org.springframework.integration.rmi.RmiOutboundGateway;
@@ -42,6 +43,8 @@ public class RmiOutboundGatewayParserTests {
private final QueueChannel testChannel = new QueueChannel();
private static volatile int adviceCalled;
@Before
public void setupTestInboundGateway() throws Exception {
testChannel.setBeanName("testChannel");
@@ -55,7 +58,7 @@ public class RmiOutboundGatewayParserTests {
public void testOrder() {
ApplicationContext context = new ClassPathXmlApplicationContext(
"rmiOutboundGatewayParserTests.xml", this.getClass());
RmiOutboundGateway gateway = context.getBean(RmiOutboundGateway.class);
RmiOutboundGateway gateway = context.getBean("gateway.handler", RmiOutboundGateway.class);
assertEquals(23, TestUtils.getPropertyValue(gateway, "order"));
}
@@ -63,11 +66,12 @@ public class RmiOutboundGatewayParserTests {
public void directInvocation() {
ApplicationContext context = new ClassPathXmlApplicationContext(
"rmiOutboundGatewayParserTests.xml", this.getClass());
MessageChannel localChannel = (MessageChannel) context.getBean("localChannel");
MessageChannel localChannel = (MessageChannel) context.getBean("advisedChannel");
localChannel.send(new GenericMessage<String>("test"));
Message<?> result = testChannel.receive(1000);
assertNotNull(result);
assertEquals("test", result.getPayload());
assertEquals(1, adviceCalled);
}
@Test //INT-1029
@@ -93,4 +97,13 @@ public class RmiOutboundGatewayParserTests {
assertEquals("TEST", result.getPayload());
}
public static class FooAdvice extends AbstractRequestHandlerAdvice {
@Override
protected Object doInvoke(ExecutionCallback callback, Object target, Message<?> message) throws Exception {
adviceCalled++;
return callback.execute();
}
}
}

View File

@@ -18,6 +18,17 @@
remote-channel="testChannel"
host="localhost"/>
<channel id="advisedChannel"/>
<rmi:outbound-gateway id="advised"
request-channel="advisedChannel"
remote-channel="testChannel"
host="localhost">
<rmi:request-handler-advice-chain>
<beans:bean class="org.springframework.integration.rmi.config.RmiOutboundGatewayParserTests$FooAdvice" />
</rmi:request-handler-advice-chain>
</rmi:outbound-gateway>
<chain input-channel="rmiOutboundGatewayInsideChain">
<rmi:outbound-gateway remote-channel="testChannel" host="localhost"/>
</chain>

View File

@@ -21,7 +21,9 @@
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="base-sftp-adapter-type">
<xsd:all>
<xsd:element name="request-handler-advice-chain" type="integration:adviceChainType" minOccurs="0" maxOccurs="1" />
</xsd:all>
<xsd:attribute name="remote-directory-expression"
type="xsd:string">
<xsd:annotation>
@@ -235,7 +237,9 @@
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="base-adapter-type">
<xsd:all>
<xsd:element name="request-handler-advice-chain" type="integration:adviceChainType" minOccurs="0" maxOccurs="1" />
</xsd:all>
<xsd:attribute name="command" use="required" type="xsd:string">
<xsd:annotation>
<xsd:documentation>

View File

@@ -50,6 +50,17 @@
charset="UTF-8"
use-temporary-file-name="false"
remote-directory="foo/bar"/>
<int-sftp:outbound-channel-adapter id="advised"
session-factory="sftpSessionFactory"
channel="inputChannel"
charset="UTF-8"
use-temporary-file-name="false"
remote-directory="foo/bar">
<int-sftp:request-handler-advice-chain>
<bean class="org.springframework.integration.sftp.config.OutboundChannelAdapterParserTests$FooAdvice" />
</int-sftp:request-handler-advice-chain>
</int-sftp:outbound-channel-adapter>
<bean id="fileNameGenerator" class="org.mockito.Mockito" factory-method="mock">
<constructor-arg value="org.springframework.integration.file.FileNameGenerator"/>

Some files were not shown because too many files have changed in this diff Show More