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:
Gary Russell
2012-07-14 13:00:38 -04:00
committed by Oleg Zhurakousky
parent 56e3c22970
commit 08cbab08c2
114 changed files with 2661 additions and 296 deletions

View File

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

View File

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

View File

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

View File

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

View File

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