I implemented a sftp-inbound-channel-adapter, and when an exception is handled , i should display a customized message.
I tried :
<int-sftp:inbound-channel-adapter id="sftpInbondAdapter"
auto-startup="true" channel="receiveChannel" session-factory="sftpSessionFactory"
local-directory="file:${directory.files.local}" remote-directory="${directory.files.remote}"
auto-create-local-directory="true" delete-remote-files="true"
filename-pattern="*.txt" >
<int:poller fixed-delay="${sftp.interval.request}"
max-messages-per-poll="-1" />
<int-sftp:request-handler-advice-chain>
<bean: class="org.springframework.integration.handler.advice.ExpressionEvaluatingRequestHandlerAdvice">
<property name="onSuccessExpression" value="payload" />
<property name="successChannel" ref="afterSuccessDeleteChannel" />
<property name="onFailureExpression" value="payload.renameTo(new java.io.File(payload.absolutePath + '.failed.to.send'))" />
<property name="failureChannel" ref="afterFailRenameChannel" />
</bean>
</int-sftp:request-handler-advice-chain>
But an element
<int-sftp:request-handler-advice-chain>
is not accepted. Can you explain another solution?
The request handler advice goes on some downstream component, not an inbound channel adapter.
You can add an error-channel to the <poller/> element. The message sent to the error channel will be an ErrorMessage with the exception as a payload. If it's an exception on the downstream flow, the payload will be a MessagingException with failedMessage and cause properties.
Add some component to consume the error messages.
Related
<int:publish-subscribe-channel id="validateChannel" apply-sequence="true">
<int:interceptors>
<int:wire-tap channel="validateLogger" />
</int:interceptors>
</int:publish-subscribe-channel>
<int:logging-channel-adapter id="validateLogger" level="INFO" />
<int:bridge input-channel="validateChannel" output-channel="validateRequestOutputChannel" />
<int:bridge input-channel="validateChannel" output-channel="externalServiceChannel" />
<int:channel id="externalServiceChannel" />
I have PublishSubscribeChannel and two sequential subscribers to it. there are calls to 2 external apis.
Step 1 (call to first external api), this api will throw exception or send 200 OK, I want to call second external api(step 2) if this api throws 200 Ok or if throws exception, I want to catch it and throw custom exception to the end user.
<int:service-activator input-channel="validateRequestOutputChannel" ref="sampleTransformer" method="preprocessRequest" output-channel="testServiceRequestChannel"/>
<int-http:outbound-gateway id="testService"
url-expression="headers.testServiceURL"
http-method="POST" request-channel="testServiceRequestChannel" reply-channel="testResponseChannel"
charset="UTF-8"
extract-request-payload="true" expected-response-type="java.lang.String"
request-factory="customHttpRequestFactory"
mapped-request-headers="Content-Type:application/json"
reply-timeout="5000">
<int-http:request-handler-advice-chain>
<bean class="org.springframework.integration.handler.advice.ExpressionEvaluatingRequestHandlerAdvice">
<property name="onSuccessExpressionString" value="payload.delete()" />
<property name="successChannel" ref="afterSuccessFetchChannel" />
<property name="failureChannel" ref="afterFailFetchChannel" />
<property name="onFailureExpressionString" value="payload + ' was bad, with reason: ' + #exception.cause.message" />
</bean>
</int-http:request-handler-advice-chain>
</int-http:outbound-gateway>
<int:transformer input-channel="afterSuccessFetchChannel" output-channel="goodResultChannel1"
expression="'Fetching service : ' + payload + ' details was successful'" />
<int:transformer input-channel="afterFailFetchChannel" output-channel="badResultChannel1" ref="exceptionTransformer" method="handleErrorResponse"/>
<int:logging-channel-adapter id="badResultChannel1" level="ERROR"/>
<int:logging-channel-adapter id="goodResultChannel1" level="INFO" />
Step in between, for the step 2, I am using input channel as externalServiceChannel which is subscribe channel to pub sub channel but I am not able to figure out how to connect output channel of step 1 to input channel of step 2,
I was trying to use,
<int:exception-type-router input-channel="testResponseChannel" default-output-channel="errorRecoveryChannel">
<int:mapping exception-type="org.springframework.web.client.HttpClientErrorException.Conflict"
channel="lockServiceErrors"/>
</int:exception-type-router>
<int:chain input-channel="lockServiceErrors" output-channel="validateOutputChannel">
<int:header-enricher>
<int:header name="http_statusCode" value="409" />
</int:header-enricher>
<int:transformer expression="payload.failedMessage" />
</int:chain>
but the issue above is
first api sends 200 Ok, its sending its response payload in the message(which I don't want, I want to re use the one from pub sub channel)
I tried using ignoreFailures = true, then in case of exceptions, it suppresses exceptions from the first api and proceed to the second one, but I want to handle exceptions(it does not even call the method from exception transformer).
I tried <property name="onSuccessExpressionString" value="payload.delete()" /> but looks like it does not actually delete the payload.
Could you please help?
Step 2(call to second external api):
<int:chain id="test-chain" input-channel="externalServiceChannel" output-channel="validateOutputChannel">
<int:transformer ref="sampleTransformer" method="preprocessAPIInfo" />
<int-http:outbound-gateway id="testService2"
url-expression="headers.testService2URL"
http-method="GET"
extract-request-payload="false"
expected-response-type="com.bibo.test.UserInfo"
charset="UTF-8"
request-factory="customHttpRequestFactory"
mapped-request-headers="Content-Type:application/json,Accept:application/json"
reply-timeout="5000">
<int-http:uri-variable name="userId" expression="headers.userId" />
</int-http:outbound-gateway>
<int:transformer ref="sampleTransformer" method="processUserInfo" />
<int:object-to-json-transformer/>
</int:chain>
I suggest you to revise you flow without a publish-subscribe channel, but really make it linear: call second service after the first. And if first step fails, it is not going to the second which is really an output of the first.
To keep an original request access in the second step there is a trick: place this request into headers:
<int:header-enricher>
<int:header name="originalRequest" expression="payload"/>
</int:header-enricher>
Then when you need it in the second step you just do like this:
<int:transformer expression="headers.originalRequest"/>
The rest of error handling logic is probably OK.
I have a gateway implementation as below. I have defined an error-channel in the gateway to trap any exception in the entire SI flow. The outputChannel sends the response back out of the gateway. As you can see there are 3 parallel flows in the system created using splitter and router.
In ErrorHandlingService, I set the appropriate error codes and send the message back to the aggregateDSLResponseChannel so that the entire flow completes smoothly. I need an implementation where entire flow ends in DSLFlowEndService even when there is an exception and then return response back to the caller by settings appropriate response object.The reason is if there was a request to connect to say WLDP and IRIS and say WLDP flow failed but IRIS succeeds, I don't want to send complete response as failure. Instead I would send an appropriate message back to the caller mentioning partial success (with details of what failed and succeeded)
To test, I introduced an exception in WLDPFlowEndService. The exception comes to error channel and then message also travels back to aggregateDSLResponseChannel and then to DSLFlowEndService. However the response is not sent back to the caller outside gateway. Basically Spring integration flow never completes and times out after 30 secs as configured. Below is the warning that comes in the logs:
15:13:23.271 [dslParallelExecutor-2] WARN org.springframework.integration.core.MessagingTemplate$TemporaryReplyChannel {} - Reply message being sent, but the receiving thread has already received a reply:[Payload=DSLResponseVO
15:13:23.271 [dslParallelExecutor-2] DEBUG org.springframework.integration.channel.DirectChannel {} - postSend (sent=true) on channel 'outputChannel', message: [Payload=DSLResponseVO
**Updated configuration- one that works (super thanks to Gary) **:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:int="http://www.springframework.org/schema/integration"
xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration
http://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd">
<int:channel id="inputChannel"></int:channel>
<int:channel id="outputChannel"></int:channel>
<int:channel id="dslFlowInitiatorOutputChannel"/>
<int:channel id="routingChannel"/>
<int:channel id="wldpInputChannel">
<int:dispatcher task-executor="wldpParallelExecutor" />
</int:channel>
<int:channel id="lcInputChannel">
<int:dispatcher task-executor="lcParallelExecutor" />
</int:channel>
<int:channel id="irisInputChannel">
<int:dispatcher task-executor="irisParallelExecutor" />
</int:channel>
<int:publish-subscribe-channel id="aggregateDSLResponseChannel"></int:publish-subscribe-channel>
<int:channel id="aggregateDSLOutputChannel">
<int:dispatcher task-executor="dslParallelExecutor" />
</int:channel>
<!-- all thread pools to execute tasks in parallel -->
<task:executor id="dslParallelExecutor" pool-size="50" />
<task:executor id="wldpParallelExecutor" pool-size="25" />
<task:executor id="lcParallelExecutor" pool-size="25" />
<task:executor id="irisParallelExecutor" pool-size="25" />
<int:gateway id="dslServiceFacade" service-interface="com.mycompany.tss.ls.dsl.gateway.IDSLServiceFacade"
default-request-channel="inputChannel" default-reply-channel="outputChannel" error-channel="gatewayErrorChannel" async-executor="dslParallelExecutor">
<int:method name="invoke" request-channel="inputChannel" request-timeout="5000" reply-timeout="3000000"/>
</int:gateway>
<int:service-activator input-channel="inputChannel"
output-channel="dslFlowInitiatorOutputChannel" ref="dslFlowInitiatorService"
method="invoke" id="dslFlowInitiator" />
<bean id="dslFlowInitiatorService" class="com.mycompany.tss.ls.dsl.service.DSLFlowInitiatorService" />
<int:splitter id="systemSplitter" input-channel="dslFlowInitiatorOutputChannel" method="split"
output-channel="routingChannel" ref="systemMessageSplitter">
</int:splitter>
<bean id="systemMessageSplitter" class="com.mycompany.tss.ls.dsl.splitter.SystemMessageSplitter"/>
<int:router id="systemRouter" input-channel="routingChannel" default-output-channel="nullChannel"
expression="headers.get('systemId')">
<int:mapping value="WLDP" channel="wldpInputChannel" />
<int:mapping value="LC" channel="lcInputChannel" />
<int:mapping value="IRIS" channel="irisInputChannel" />
</int:router>
<int:aggregator id="dslResponseAggregator" input-channel="aggregateDSLResponseChannel" output-channel="aggregateDSLOutputChannel"
message-store="dslResponseMessageStore" correlation-strategy-expression="headers['requestId']"
send-partial-result-on-expiry="true">
</int:aggregator>
<int:service-activator input-channel="aggregateDSLOutputChannel"
output-channel="outputChannel" ref="dslFlowEndService"
method="invoke" id="dslFlowEndActivator" />
<bean id="dslFlowEndService" class="com.mycompany.tss.ls.dsl.service.DSLFlowEndService" />
<bean id="dslResponseMessageStore" class="org.springframework.integration.store.SimpleMessageStore" />
<bean id="dslResponseMessageStoreReaper" class="org.springframework.integration.store.MessageGroupStoreReaper">
<property name="messageGroupStore" ref="dslResponseMessageStore" />
<property name="timeout" value="4000" />
</bean>
<task:scheduled-tasks>
<task:scheduled ref="dslResponseMessageStoreReaper" method="run" fixed-rate="10000" />
</task:scheduled-tasks>
<int:wire-tap pattern="*Channel" order="3" channel="wiretap"/>
<int:message-history/>
<int:logging-channel-adapter id="wiretap" level="DEBUG"/>
<!-- Error handling Service Activator to log exceptions and send the response -->
<int:service-activator id="errorHandlingServiceActivator" input-channel="gatewayErrorChannel"
method="invoke" output-channel="aggregateDSLResponseChannel" ref="errorHandlingService">
</int:service-activator>
<!-- Error Handling Service class -->
<bean id="errorHandlingService" class="com.mycompany.tss.ls.dsl.service.ErrorHandlingService" />
<bean id="wldpErrorHandlingService" class="com.mycompany.tss.ls.dsl.service.ErrorHandlingService" />
<bean id="irisErrorHandlingService" class="com.mycompany.tss.ls.dsl.service.ErrorHandlingService" />
<int:channel id="wldpStartChannel"></int:channel>
<int:channel id="irisStartChannel"></int:channel>
<int:channel id="wldpErrorChannel"></int:channel>
<int:channel id="irisErrorChannel"></int:channel>
<int:channel id="wldpOutputChannel"></int:channel>
<int:channel id="irisOutputChannel"></int:channel>
<int:service-activator id="midFlowWLDPActivator" input-channel="wldpInputChannel"
ref="midFlowWLDPGateway" output-channel="aggregateDSLResponseChannel"/>
<int:service-activator id="midFlowIRISActivator" input-channel="irisInputChannel"
ref="midFlowIRISGateway" output-channel="aggregateDSLResponseChannel"/>
<int:gateway id="midFlowWLDPGateway" default-request-channel="wldpStartChannel"
service-interface="com.mycompany.tss.ls.dsl.gateway.IDSLWLDPFacade" error-channel="wldpErrorChannel"
/>
<int:gateway id="midFlowIRISGateway" default-request-channel="irisStartChannel"
service-interface="com.mycompany.tss.ls.dsl.gateway.IDSLIRISFacade" error-channel="irisErrorChannel"
/>
<int:service-activator id="errorHandlingWLDPServiceActivator" input-channel="wldpErrorChannel"
method="invoke" ref="wldpErrorHandlingService">
</int:service-activator>
<int:service-activator id="errorHandlingIRISServiceActivator" input-channel="irisErrorChannel"
method="invoke" ref="irisErrorHandlingService">
</int:service-activator>
<!-- Responsible for closing all the activities related to WLDP before sending response -->
<int:service-activator id="wldpFlowEndServiceActivator" input-channel="wldpStartChannel"
method="invoke" ref="wldpFlowEndService">
</int:service-activator>
<bean id="wldpFlowEndService" class="com.mycompany.tss.ls.dsl.service.wldp.WLDPFlowEndService" />
<!-- Responsible for closing all the activities related to IRIS before sending response -->
<int:service-activator id="irisFlowEndServiceActivator" input-channel="irisStartChannel"
method="invoke">
</int:service-activator>
<bean id="irisFlowEndService" class="com.mycompany.tss.ls.dsl.service.iris.IrisFlowEndService" />
ErrorHandlingService:
public class ErrorHandlingService {
Logger logger = LoggerFactory.getLogger(ErrorHandlingService.class);
public Message<DSLResponseVO> invoke(Message<?> requestMessage) {
Object exception = requestMessage.getPayload();
if(exception instanceof MessagingException){
DSLResponseVO responseVO = new DSLResponseVO();
logger.error("Exception has occurred: {}", ((MessagingException)exception).getMessage());
logger.error("Exception Stacktrace is: ", exception);
responseVO.setRequestId((String)((MessagingException)exception).getFailedMessage().getHeaders().get("requestId"));
responseVO.setSystemId((String)((MessagingException)exception).getFailedMessage().getHeaders().get("systemId"));
responseVO.setErrorCode(DSLErrorConstants.FAILURE_CODE);
responseVO.setErrorMessage(DSLErrorConstants.FAILURE_DESC);
Message<DSLResponseVO> failedMessage = MessageBuilder.withPayload(responseVO)
//.copyHeadersIfAbsent(((MessagingException)exception).getFailedMessage().getHeaders())
.setHeaderIfAbsent("requestId", (String)((MessagingException)exception).getFailedMessage().getHeaders().get("requestId"))
.setHeaderIfAbsent("systemId", (String)((MessagingException)exception).getFailedMessage().getHeaders().get("systemId"))
.setHeaderIfAbsent(MessageHeaders.SEQUENCE_NUMBER, (Integer)((MessagingException)exception).getFailedMessage().getHeaders().get(MessageHeaders.SEQUENCE_NUMBER))
.setHeaderIfAbsent(MessageHeaders.SEQUENCE_SIZE, (Integer)((MessagingException)exception).getFailedMessage().getHeaders().get(MessageHeaders.SEQUENCE_SIZE))
.setHeaderIfAbsent(MessageHeaders.REPLY_CHANNEL, ((MessagingException)exception).getFailedMessage().getHeaders().get(MessageHeaders.REPLY_CHANNEL))
.build();
return failedMessage;
}
return null;
}
}
You can't send multiple replies to the gateway; you need to aggregate the results (or failures) into a single reply message.
Each subflow needs to send the result, or error to and aggregator.
You need an error flow on each subflow. If you use a queue channel instead of executor channel, you can put an error-channel on the poller.
When using executor channel you need a mid-flow gateway to add an error flow (service activator that references a <gateway/>).
In the below example Spring Integration message flow, I need to be able to reprocess the original message. One of the areas I have exceptions is in the transformer or in the call to the web service at the end. So what I have seen so far is that once the payload is changed via a transformer or service, I can't roll back to the original message that was placed on the jms queue. Is there a feature that would allow for me to send the original message to a different jms queue when exceptions occur?
<int-jms:message-driven-channel-adapter
id="Jms"
connection-factory="connectionFactory"
destination="queue"
channel="XMLChannel"
error-channel="errorChannel"
/>
<int:channel id="XMLChannel"/>
<int:chain input-channel="XMLChannel" output-channel="ObjectChannel">
<int-xml:unmarshalling-transformer id="defaultUnmarshaller" unmarshaller="xmlMarshaller" />
</int:chain>
<int:channel id="ObjectChannel"/>
<int:chain id="transformerChain" input-channel="ObjectChannel" output-channel="post" >
<int:filter ref="environmentFilter" />
<int:filter ref="emailFilter" />
<int:transformer ref="transformer" />
<int:object-to-json-transformer object-mapper="objectMapperFactory" />
<int:header-enricher>
<int:header name="content-type" value="application/json"></int:header>
</int:header-enricher>
<int-http:outbound-gateway
url="${url.${runtime.environment}}"
http-method="POST"
expected-response-type="java.lang.String"
charset="UTF-8"
reply-channel="post">
<int-http:request-handler-advice-chain>
<int:retry-advice max-attempts="3">
<int:exponential-back-off initial="5000" multiplier="10.0" maximum="600000" />
</int:retry-advice>
</int-http:request-handler-advice-chain>
</int-http:outbound-gateway>
</int:chain>
<int:channel id="post"/>
<int:logging-channel-adapter channel="post" level="INFO" log-full-message="true"/>
<int:exception-type-router input-channel="addChannel" default-output-channel="errorChannel">
<int:mapping />
</int:exception-type-router>
<int:channel id="errorChannel" />
<int:chain input-channel="errorChannel">
<int:transformer ref="errorTransformer"/>
<!-- <int:object-to-json-transformer/>
<int-xml:marshalling-transformer marshaller="xmlMarshaller" /> -->
<int-jms:outbound-channel-adapter id="dcsConsumerAppointmentError"
connection-factory="connectionFactory" destination="dcsConsumerAppointmentErrorQueue" />
</int:chain>
#Transformer
public Message<?> handleFailedOrder(Message<MessageHandlingException> message) throws JAXBException, IOException {
log.info(message.getPayload().getMessage());
JAXBElement<CustomerRequestVO> test = (JAXBElement<CustomerRequestVO>) message.getPayload().getFailedMessage().getPayload();
String xml = jaxbMarshalToString(test.getValue());
Message<?> messageOut = MessageBuilder.withPayload(xml).setHeader("Exception", message.getPayload().getMessage()).build();
return messageOut;
}
It isn't clear from your config what is addChannel, but as far as you use error-channel="errorChannel" the original message (I guess it is an XML) will be sended to the errorChannel as failedMessage of the MessagingException.
If your handleFailedOrder is a <int:transformer ref="errorTransformer"/> for the errorChannel, your message.getPayload().getFailedMessage().getPayload() should be an original XML, not unmarshaled CustomerRequestVO.
Another trick to keep track of the original payload is to put it to the MessageHeaders before further processing and transformation.
However, of course, you should keep in mind that the copy of the same mutable object will be changed everywhere, you will change its property once.
The jdbc inbound channel adapter and message stores in our application consistenly stops querying for data and I've been able to track it down to a blocked Oracle session. In Oracle, we are getting enq: TX - row lock contention. The DBA tracked it down to the following:
SELECT COMPLETE, LAST_RELEASED_SEQUENCE, CREATED_DATE, UPDATED_DATE
from INT_MESSAGE_GROUP
where GROUP_KEY = :1 and REGION=:2
Any suggestions on how to resolve this would be greatly appreciated.
SI configuration (extract):
<int-jdbc:message-store id="jdbc-messageStore" data-source="dataSource" />
<int-jdbc:inbound-channel-adapter id="JDBCInboundChannel" query="${cache.integration.jdbc.selectQuery}"
channel="inboundMessagesChannel" data-source="dataSource" update="update CACHE_REPOSITORY set STATUS='P' WHERE GUID IN (:guid)" row-mapper="rowMapper"
max-rows-per-poll="${cache.integration.jdbc.maxRowsPerPoll}" auto-startup="false" >
<int:poller id="jdbcPoller" fixed-delay="${cache.integration.jdbc.fixedDelay}" >
<int:transactional />
</int:poller>
</int-jdbc:inbound-channel-adapter>
<int:chain...>
<int:aggregator id="reportTypeAggregator" ref="aggregatorBean" method="collect"
message-store="jdbc-messageStore" release-strategy="releaseStrategry"
release-strategy-method="canRelease" correlation-strategy="reportTypeCorrelationStrategry"
correlation-strategy-method="correlate" send-partial-result-on-expiry="true"
expire-groups-upon-completion="true" empty-group-min-timeout="30000" />
</int:chain>
<bean id="queryProvider" class="org.springframework.integration.jdbc.store.channel.OracleChannelMessageStoreQueryProvider"/>
<bean id="jdbc-channel-messageStore" class="org.springframework.integration.jdbc.store.JdbcChannelMessageStore">
<property name="dataSource" ref="dataSource"/>
<property name="channelMessageStoreQueryProvider" ref="queryProvider"/>
<property name="region" value="${cache.integration.channelMessageStore.region}"/>
</bean>
<int:channel id="archiveCreationChannel" >
<int:queue message-store="jdbc-channel-messageStore" />
<int:interceptors>
<int:wire-tap channel="logger" timeout="-1"/>
</int:interceptors>
</int:channel>
<bean id="documentMessageStoreReaper" class="org.springframework.integration.store.MessageGroupStoreReaper">
<property name="messageGroupStore" ref="jdbc-messageStore" />
<property name="timeout" value="${cache.integration.reaper.timeout}" />
<property name="autoStartup" value="false" />
</bean>
<task:scheduled-tasks>
<task:scheduled ref="documentMessageStoreReaper" method="run" fixed-rate="10000" />
</task:scheduled-tasks>
I suppose the main issue is around fixed-rate for documentMessageStoreReaper, which initiate the DELETE from INT_MESSAGE_GROUP. You should change it to the fixed-dealy to avoid concurrent tasks.
Since you say that the DELETE from INT_MESSAGE_GROUP is very long, hence 10 sec between tasks might be not enough.
I am trying to create a generic Error Handling process for All message Handlers that will be used in my SI flow. This will,
1. Retry on Connection Exception.
2. Stop the SI flow using Circuit Breaker.
3. Rollback the failed message to the channel.
I have achieved the Retry and Circuit Break Functionality. But, I am unable to rollback message to the channel.
I tried using transaction advice. But it does not work.
Here is the code.
<bean id="retryAdvice"
class="org.springframework.integration.handler.advice.RequestHandlerRetryAdvice">
<property name="retryTemplate">
<bean class="org.springframework.retry.support.RetryTemplate">
<property name="backOffPolicy">
<bean class="org.springframework.retry.backoff.ExponentialBackOffPolicy">
<property name="initialInterval" value="2000" />
<property name="multiplier" value="2" />
</bean>
</property>
</bean>
</property>
<property name="recoveryCallback">
<bean
class="org.springframework.integration.handler.advice.ErrorMessageSendingRecoverer">
<constructor-arg ref="recoveryChannel" />
</bean>
</property>
<property name="retryStateGenerator">
<bean
class="org.springframework.integration.handler.advice.SpelExpressionRetryStateGenerator">
<constructor-arg value="headers['uniqueId']" />
</bean>
</property>
</bean>
<int:channel id="recoveryChannel" />
<int:transformer id="defaultTransformer" input-channel="recoveryChannel"
output-channel="loggerChannel" ref="defaultTransformer" method="transform">
</int:transformer>
<int:logging-channel-adapter id="loggerChannel"
level="INFO" log-full-message="true" auto-startup="true">
</int:logging-channel-adapter>
<bean id="defaultTransformer"
class="com.bestbuy.ingestion.foundation.core.util.DefaultTransformer" />
<bean id="circuitBreakerAdvice"
class="org.springframework.integration.handler.advice.RequestHandlerCircuitBreakerAdvice">
<property name="threshold" value="2" /> <!-- close after 2 failures -->
<property name="halfOpenAfter" value="60000" /> <!-- half open after 15 seconds -->
</bean>
<tx:advice id="txansactionAdvice" transaction-manager="transactionManager">
</tx:advice>
What type of Transaction Manager I need to use.
I may be using different message Handlers, on different data source.
Here is how I add these advises to the Message Handlers.
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
logger.error("called for bean id :: "+beanName+" with bean class "+bean.getClass().getName());
if(bean instanceof AbstractSimpleMessageHandlerFactoryBean){
logger.error("************ Bean "+beanName+" is instance of AbstractSimpleMessageHandlerFactoryBean **********");
}
if(bean instanceof ConsumerEndpointFactoryBean){
logger.error("Bean is of type ConsumerEndpointFactoryBean");
return populateRequestHandlerAdviceChain((ConsumerEndpointFactoryBean)bean);
}
if(bean instanceof AbstractSimpleMessageHandlerFactoryBean){
logger.error("Bean is of type AbstractSimpleMessageHandlerFactoryBean");
return populateRequestHandlerAdviceChain((AbstractSimpleMessageHandlerFactoryBean<?>)bean);
}
return bean;
}
private Object populateRequestHandlerAdviceChain(ConsumerEndpointFactoryBean bean){
ArrayList<Advice> list = new ArrayList<Advice>();
logger.error("Adding Retry Advice");
list.add((Advice)factory.getBean("retryAdvice"));
logger.error("Adding Cricuit Breaker Advice");
list.add((Advice)factory.getBean("circuitBreakerAdvice"));
logger.error("Adding Transactional Advice");
list.add((Advice)factory.getBean("txansactionAdvice"));
bean.setAdviceChain(list);
return bean;
}
If the bean of type ConsumerEndpointFactoryBean I add these advices. I need transaction management in all these Handlers.
First of all: since your txansactionAdvice is nested to the retryAdvice you rollback here the each retry.
From other side it isn't clear why you apply the circuitBreakerAdvice for each retry. I'd say this patter would be better to use 'around' retryAdvice.
And the txansactionAdvice should on top. So, it might look like this:
txansactionAdvice
circuitBreakerAdvice
retryAdvice
And one more point: your transaction won't be rolled back, bacause your use recoveryCallback, which just sends ErrorMessage and strangles the Exception.
HTH and you'll change your mind on the matter.