I have an integration flow to poll json files from some folder and send them to a rest http endpoint. Below my code:
#Bean
public IntegrationFlow jsonFileToRestFlow() {
return IntegrationFlows
.from("fileInputChannel")
.transform(new FileToStringTransformer())
.enrichHeaders(s -> s.header("Content-Type", "application/json; charset=utf8"))
.handle(httpRequestExecutingMessageHandler())
.get();
}
After receiving response from http endpoint i will move my files to successful or failure channel. Now i want to test my code. What is the best way to test this code. My idea is to put some json Files in my inputChannel and then mock my http response and check if expected message is in successChannel or failure. But I don't know how to start. Can anyone give me some tips? thanks
Please, take a look into the Spring Integration Testing Framework. So, your httpRequestExecutingMessageHandler can be replaces with the MockIntegration.mockMessageHandler() where you really can produce any possible mocked reply in the handleNextAndReply().
Another option is like a MockMvc and its MockMvcClientHttpRequestFactory to be injected into the RestTemplate for the HttpRequestExecutingMessageHandler.
Success or failure can be achieved with the ExpressionEvaluatingRequestHandlerAdvice applied on the .handle(httpRequestExecutingMessageHandler()) endpoint.
Related
I have to listen a queue using spring integration flow and intgeration sqs. Once message is received from queue it should trigger a integration flow. Below is the things which I am trying but everythings fine in but afater receiving test it is not triggering any Integration flow. Please let me know where I am doing wrong:
UPDATED as per comment from Artem
Adapter for SQS.
#Bean
public MessageProducerSupport sqsMessageDrivenChannelAdapter() {
SqsMessageDrivenChannelAdapter adapter = new SqsMessageDrivenChannelAdapter(amazonSQSAsync, "Main");
adapter.setOutputChannel(inputChannel());
adapter.setAutoStartup(true);
adapter.setMessageDeletionPolicy(SqsMessageDeletionPolicy.NEVER);
adapter.setMaxNumberOfMessages(10);
adapter.setVisibilityTimeout(5);
return adapter;
}
Queue configured:
#Bean
public MessageChannel inputChannel() {
return new DirectChannel();
}
Now the main integration flow trigger point:
#Bean
public IntegrationFlow inbound() {
return IntegrationFlows.from("inputChannel").transform(i -> "TEST_FLOW").get();
}
}
Appreciate any type of help.
The sqsMessageDrivenChannelAdapter() must be declared as a #Bean
The inbound() must be declared as a #Bean
This one fully does not make sense IntegrationFlows.from(MessageChannels.queue()). What is the point to start the flow from anonymous channel? Who and how is going to produce messages to that channel?
Make yourself familiar with different channels: https://docs.spring.io/spring-integration/docs/current/reference/html/core.html#channel-implementations
Pay attention that QueueChannel must be consumed via polling endpoint.
Right, there is a default poller auto-configured by Spring Boot, but it is based on a single thread in the TaskScheduler and has a polling period as 10 millis.
I wouldn't recommend to hand off SQS messages to the QueueChannel: when consumer fails, you lose the data. It is better to process those messages in the consumer thread.
Otherwise your intention is not clear in the provided code.
Can you, please, share with us what error you get or anything else?
You also can turn on DEBUG logging level for org.springframework.integration to see how your messages are processed.
I am trying to catch the Exceptions thrown from the RestTemplate (500 error) when used with a Http.OutboundGateway and send to a failure Channel or success channel. I have tried all sorts before coming back to stackoverflow to ask for help (tried to route by payload, errorhandlers) but i just can't seem to get this to work in a integration test. The success or failure should send message to a channels and then the net flow with use a JdbcMessageHandler and updates the DB accordingly.
IntegrationFlows.from(updateSuccessChannel)
.log(message -> message)
.handle(Http.outboundGateway(updateTicketURI, restTemplate)
.httpMethod(HttpMethod.PUT), out -> out.advice(expressionAdvice()))
.get();
public Advice expressionAdvice() {
final ExpressionEvaluatingRequestHandlerAdvice advice = new ExpressionEvaluatingRequestHandlerAdvice();
advice.setReturnFailureExpressionResult(true);
advice.setFailureChannel(failureChannel);
advice.setSuccessChannel(successChannel);
advice.setOnFailureExpressionString("#exception");
advice.setTrapException(true);
return advice;
}
I am using Wiremock to return the status code in the unit test and am mocking the JdbcMessageHandler so i can see the interactions with them.
Can someone point me in the right direction as to handle the failures thrown from a restTemplate, I have tried implementing a response error handler for the reset template but I don't see how I can get the original message when back in the work flow.
Any help gratefully received this seems to be a re-occurring pattern and I just can't seem to get this to work :(
Now I accept an http request to handle time-consuming tasks. So i want to reply the requester first and then use other flow async to handler it.
I try to use CompletableFuture.runAsync in a handler() method and then use MessageChannel.send to do . But I think there should be a more elegant way
#Bean
public IntegrationFlow testFlow(){
return IntegrationFlows.from("requestChannel")//accept an request
.handle(handlerSomeThing())//do something
.handle()?// how to send message to main and reply quickly
.enrichHeaders(c->c.header(HttpHeaders.STATUS_CODE,HttpStatus.OK))
.get();
}
#Bean
public IntegrationFlow mainFlow(){
return IntegrationFlows.from("mainChannel")//accept an request
.handleMainLogic()//handler main logic
.get();
}
User a publishSubscribeChannel.
See this answer.
Add a task executor so that the two subflows run in parallel.
How to route the flow from the channel myChannel to the error channel myErrorChannel if there occurs an error in the Http.outboundGateway call?
#Bean
private IntegrationFlow myChannel() {
return f -> f
.handle(Http.outboundGateway("http://localhost:8080/greeting")
...
.expectedResponseType(String.class));
}
#Bean
private IntegrationFlow myErrorChannel() {
return f -> f
...
}
In the error handler I will wrap the error message inside my custom JSON and I will send that as a part of the normal flow back to the source system.
Is this a good way to handle errors in the Spring Integration Java DSL?
You can use an ExpressionEvaluatingRequestHandlerAdvice with its returnFailureExpressionResult = true and use it in the second argument of the .handle(..., e -> e.advice(...)).
You configure that advice for the onFailureExpression to be able to return something meaningful. If you still think that you need to send to the channel and get reply back, then you need to have a #MessagingGateway and use it in that onFailureExpression to send and receive. The normal failureChannel configuration in the ExpressionEvaluatingRequestHandlerAdvice doesn't expect reply.
Another approach can be done using the same #MessagingGateway, but in front of that myChannel IntegrationFlow. Then you can configure that gateway for the errorChannel and here a reply from the error flow is expected.
I am using
MessageProducerSupport messageProducer =
Jms.messageDriverChannelAdapter(jmsConnectionFactory, TransactedMessageListenerContainer.class)
.destination(queue)
.get();
to consume messages from ActiveMQ queue.
This is first part of my IntegrationFlow and then multiple stages occur (transform, route, handle..) within transaction
It is there to handle messages from upstream
In order to get the ACK from Spring integration pipeline I used Jms.inboundGateway(jmsConnectionFactory, TransactedMessageListenerContainer.class) which doesn't break existing flow and everything works
When I set replyTo header of upstream message, I would assume Spring Integration would send the object of the last stage of IntegrationFlow which was successful back to replyTo queue
Is my approach correct?
Is it possible to achieve such use-case?
Yes, that's correct and should work by its (Messaging Gateway) premise.
The Jms.inboundGateway() is based on the ChannelPublishingJmsMessageListener with the expectReply = true and there is a code:
private Destination getReplyDestination(javax.jms.Message request, Session session) throws JMSException {
Destination replyTo = request.getJMSReplyTo();
....
return replyTo;
}
to obtain a replyTo from the request.
Everything that works well, if your last MessageHandler in the flow is a AbstractReplyProducingMessageHandler and really returns something to be produced to the replyChannel from headers.
If you aren't sure in your case, so share the end of your flow and the place where would you like to send a reply.