INT-2214, INT-343, INT-2250 MessageHandler Advice
Add general capability to advise just the handleRequestMessage part of an AbstractReplyProducingMessageHandler. This is to advise just the immediate operation, and not the entire downstream flow. Uses include: * outbound gateway post processing * adding retry behavior using spring-retry * adding circuit breaker functionality Initial commit for review. Also need to advise simple message handlers (such as file etc) to allow them to post-process file operations with payload.delete(), payload.renameTo(...) etc. INT-2250 Add Circuit Breaker Advice INT-343 Add Retry Advice Stateless and Stateful retry using spring-retry. Stateless means the RetryTemplate performs the retries internally. Stateful means the exception is thrown (e.g. to JMS container) and the retry state is maintained by spring-retry. INT-2215, INT-343, INT-2250 Refactoring Factor out common abstract Advice class. INT-2214 Catch Evaluation Expression Exceptions If an onSuccess expression evaluation fails, add an option so the user can decide whether such an exception is caught, or propagated to the caller. INT-2214 etc PR Review Polishing INT-2214 etc Namespace Core, File, FTP Add <request-handler-advice-chain/> to outbound endpoints. INT-2214 etc. More Namespace Support amqp, event, gemfire, groovy, http, ip, jdbc, jms, jmx, jpa, mail, rmi, sftp, twitter, ws, xmpp INT-2214 etc Polishing PR Review INT-2214 etc Polishing Don't catch Throwable. Move Advice classes to handler.advice package.
This commit is contained in:
committed by
Oleg Zhurakousky
parent
56e3c22970
commit
08cbab08c2
@@ -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