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:
@@ -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',
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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/>
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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 <poller> 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) {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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">
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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"/>
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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/>
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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 <outbound-channel-adapter/> 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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'"
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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" />
|
||||
|
||||
|
||||
|
||||
@@ -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>
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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" />
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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'.");
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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]"
|
||||
|
||||
@@ -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" />
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -103,6 +103,7 @@ public class OperationInvokingMessageHandler extends AbstractReplyProducingMessa
|
||||
@Override
|
||||
public final void onInit() {
|
||||
Assert.notNull(this.server, "MBeanServer is required.");
|
||||
super.onInit();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
/**
|
||||
* <transactional /> element applies to entire flow from this point
|
||||
*/
|
||||
private volatile List<Advice> txAdviceChain;
|
||||
|
||||
/**
|
||||
* <request-handler-advice-chain /> 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user