Intermittent BridgeHandler & PublishSubscribeChannel call when gateways' reply channel is pub/sub - spring-integration

I'm seeing weird behaviour when sending data through my channels. I'm using SI gateway when sending a message to be processed. The gateway is setup as below
<integration:gateway id="rptPubGateway"
default-request-channel="rptPubInChannel"
default-reply-channel="rptOutputAvailableChannel"
default-reply-timeout="60000"
service-interface="xxxx.RptPubGateway" />
The reply channel is being set up as a publish/subscribe channel
<integration:publish-subscribe-channel id="rptOutputAvailableChannel" />
The last service that processes the message is being declared as below
<integration:service-activator input-channel="rptOutputAvailableChannel" ref="requestMessageHandler" method="rptIsDone" output-channel="nullChannel"/>
Now, the issue that i have is that while the code works fine most of the time, it fails sometimes. When everything works fine the last component processing my message is PublishSubsChannel
PublishSubscribeChannel - preSend on channel 'rptOutputAvailableChannel'
but when it fails the last component becomes BridgerHandler
BridgeHandler#e851a798' sending reply Message:
I should mention that there are no exceptions being thrown while processing my message. (after the failure I can always resend the same message and everything will work OK)
I'm not sure how that BridgerHandler gets created. From what I know this bridge gets created for pub/subs channels but then why I don't see it in the log when everything works fine ?
I'd appreciate any help

When you say "fails" what do you mean?
What are the other consumers on rptOutputAvailableChannel?
The BridgeHandler you see is an internal bridge to the message's replyChannel header, which is where the reply ultimately must go. When explicitly sending to rptOutputAvailableChannel, the bridge handler is always invoked to get the reply to the message's temporary reply channel.

Related

Azure Service Bus - random deserialization issues

I've been recently having problems with my Service Bus queue. Random messages (one can pass and the other not) are placed on the deadletter queue with the error message saying:
"DeadLetterReason": "Moved because of Unable to get Message content There was an error deserializing the object of type System.String. The input source is not correctly formatted."
"DeadLetterErrorDescription": "Des"
This happens even before my consumer has the chance to receive the message from the queue.
The weird part is that when I requeue the message through Service Bus Explorer it passes and is successfully received and handled by my consumer.
I am using the same version of Service Bus either for sending and receiving the messages:
Azure.Messaging.ServiceBus, version: 7.2.1
My message is being sent like this:
await using var client = new ServiceBusClient(connString);
var sender = client.CreateSender(endpointName);
var message = new ServiceBusMessage(serializedMessage);
await sender.SendMessageAsync(message).ConfigureAwait(true);
So the solution I have for now for the described issue is that I implemented a retry policy for the messages that land on the dead-letter queue. The message is cloned from the DLQ and added again to the ServiceBus queue and for the second time there is no problems and the message completes successfully. I suppose that this happens because of some weird performance issues I might have in the Azure infrastructure. But this approach bought me some time to investigate further.

Channel closed by server: 406 (PRECONDITION-FAILED) with message "PRECONDITION_FAILED - inequivalent arg 'x-max-priority' for queue 'xyz'

While running the app in consumer mode, my application is frequently crashing with an error Error: Channel closed by server: 406 (PRECONDITION-FAILED) with message "PRECONDITION_FAILED - inequivalent arg 'x-max-priority' for queue 'xyz' in vhost '/': received the value '10' of type 'byte' but current is none". Does anyone know the fix for this issue?
#here Deleting the queue 'xyz' worked for me.
It appears that you're making a call to a server that requires one or more preconditions to be met before it will return a result. Since this is a remote server call, you'd need to check the API documentation to determine what precondition(s) is/are required, or if there is no API documentation, contact the manager of the server in question.
See 406 HTTP "Not Acceptable" Status Code for more about this particular error.
While the exception of assertQueue still exists, but I found that I don't need to run assertqueue before consuming .. so for now I just commented the assertQueue call. and consuming is working.
I don't know why all examples of ameplib run assertQueue before anything?!
406 ERROR also may occur if the queueNamed channel is acknowledeged more than once, Make sure channel ack type is set to manual, {noAck: false}. this closes the queueChannel which causes the consumer/publisher to not be able to access the queueChannel.
Note: Make sure you ack the queue in the same order it is received by the system. If your handling many publishers and consumers in single functionality also check if you have pre acked the channel.

JMS Message body is null when publishing with jms:publish-subscribe-channel

I am trying to use jms:publish-subscribe-channel to pub/sub on a single ActiveMQ topic. I am able to receive messages from ActiveMQ on the channel just fine, however when I publish to the channel the message body is null (when received by another application listening on the ActiveMQ topic). I was able to recreate the problem using spring-integration-samples->basic->jms. I modified outboundChannelAdapter.xml to use jms:publish-subscribe-channel instead of jms:outbound-channel-adapter. Is there another step needed in order to publish a simple string message? Here's my code change to outboundChannelAdapter.xml:
<stream:stdin-channel-adapter id="stdin" channel="stdinToJmsoutChannel"/>
<jms:publish-subscribe-channel id="stdinToJmsoutChannel" topic="requestTopic" />
<stream:stdout-channel-adapter id="stdout" channel="stdinToJmsoutChannel" append-newline="true"/>
I am not sure what you mean by "the message body is null".
I just made the exact same change to the sample and it worked fine for me...
Please type something and hit <enter>
foo
foo
I had to add -Dorg.apache.activemq.SERIALIZABLE_PACKAGES=* to the command line because activemq needs whitelisting for classes (the whole message is serialized in jms-backed channels).

Spring JMS Outbound Gateway receive timeout being ignored

I have a Spring Integration flow which sends a message out via a JMS Outbound Gateway which is configured to have a receive timeout of 45 seconds. I am trying to test the receive timeout period by sending a message out in a setup where the message is never consumed on the other side (therefore a response doesn't come back). However, when I run the test, the message is placed in the outbound queue but, the Outbound Gateway's receive timeout never occurs (after 45 seconds). Any ideas what reasons there could be for this happening (not happening)?
My stack is:
o.s.i:spring-integration-java-dsl:1.0.0.M3
o.s.i:spring-integration-jms:4.0.4.RELEASE
My IntegrationgConfig.class:
#Configuration
#EnableIntegration
public class IntegrationConfig {
...
#Bean
public IntegrationFlow testFlow() {
return IntegrationFlows
.from("test.request.ch")
.handle(Jms.outboundGateway(connectionFactory)
.receiveTimeout(45000)
.requestDestination("REQUEST_QUEUE")
.replyDestination("RESPONSE_QUEUE")
.correlationKey("JMSCorrelationID"))
.handle("testService",
"testMethod")
.channel("test.response.ch").get();
}
...
}
In terms of JMS configuration, the connection factory used is a standard CachingConnectionFactory which targets an MQConnectionFactory.
Thanks in advance for any help on this.
PM
--- UPDATE ---
I have turned on debugging and I can see that when the timeout occurs the following message is logged:
AbstractReplyProducingMessageHandler - handler 'org.springframework.integration.jms.JmsOutboundGateway#0' produced no reply for request Message: [Payload byte[835]][...]
Just need to find out how to capture this event in the flow?
--- UPDATE 2 ---
The message being sent out has an ERROR_CHANNEL header set on it to which I would expect the timeout exception to be routed to - but this routing does not happen?
Is it possible that the CachingConnectionFactory is handling the exception and not passing it back to the flow?
To make it working you need to add the second Lambda to the .handle() with Jms:
.handle(Jms.outboundGateway(connectionFactory)
.receiveTimeout(45000)
.requestDestination("REQUEST_QUEUE")
.replyDestination("RESPONSE_QUEUE")
.correlationKey("JMSCorrelationID"),
e -> e.requiresReply(true))
By default AbstractReplyProducingMessageHandler doesn't require reply even if receiveTimeout is exhausted, and we see that by logs shown by you.
However, I see that we should revise all MessageHandlerSpecs, because XML support changes the requires-reply for some components to true by default.
Feel free to raise a JIRA issue on the matter and we'll address it soon, because the GA release for Java DSL is planned over a week or two: https://spring.io/blog/2014/10/31/spring-integration-java-dsl-1-0-rc1-released
Thank you for the attention to this stuff!

Some Spring JMS Questions

I have a question regarding JMS and Spring Integration.
I have 3 queues, let's call them QUEUE_SOURCE, QUEUE_TARGET and QUEUE_ERROR.
A DefaultMessageListenerContainer is used to read the messages from the QUEUE_SOURCE.
I have configured a JMS Transaction manager for these queues.
When I read from the QUEUE_SOURCE, but an error occurs when posting the message to QUEUE_TARGET, I
can see that the message is retried a few times before an exception is thrown, triggering a rollback.
I guess what happens is this:
1) Start transaction
2) Read message
3) Try to post message on target queue QUEUE_TARGET, but fail
4) Do not commit because of error, so message is not removed from QUEUE_SOURCE
OR
1) Start transaction
2) Read message
3) Remove message from QUEUE_SOURCE
4) Try to post message on target queue QUEUE_TARGET, but fail
5) Put message back on QUEUE_SOURCE
After all redeliveries fail, a rollback is triggered.
However, what happens during this rollback confuses me.
I have no clue where the message ends up, it certainly does not end up on the source queue, and it does not end up on the error queue either. So what is really happening during this rollback?
You would need to show your exact configuration but your speculation #1 is close; the broker delivers the message but does not remove it from the queue until the commit. If your code throws and exception, the framework will execute session.rollback() and the broker will redeliver. If the delivery is successful, the framework will commit the transaction and only then will the message be removed.

Resources