0

I have some client code that calls Spring Integration via a call to a DirectExchange, e.g.

    Map<String, Object> result = (Map<String, Object>) rabbitTemplate.convertSendAndReceive("rpc", "KEY", map);

I can see that the integration flow is called, assembles the proper result, and sends an AMQP message back out, but the message is never received. I have to use XML configuration, and I have tried various combinations of channel adapters and gateways, with varying degrees of success, but no combination that returns all the time.

Just an inbound gateway will invoke the flow, and the client code will see the response, but half of the responses are the input itself:

<int-amqp:inbound-gateway auto-startup="true"
        request-channel="requestChannel"
        reply-channel="responseChannel"
        message-converter="jacksonMessageConverter"
        queue-names="rpc.KEY"/

Channel adapters also invoke the flow, but the response is never consumed by the client code, with or without the exchange and routing expressions, though it appears that the AMQP message is assembled and published.

<int-amqp:inbound-channel-adapter auto-startup="true"
    channel="requestChannel2"
    message-converter="jacksonMessageConverter"
    mapped-request-headers="*"
    queue-names="rpc.KEY"/>

<int-amqp:outbound-channel-adapter auto-startup="true"
        mapped-request-headers="*"
        exchange-name-expression="headers.amqp_receivedExchange"
        routing-key-expression="headers.amqp_replyTo"
        channel="responseChannel2"
        amqp-template="rpcTemplate"/>

Using either approach, what configuration step am I missing?

Edit: full sample context; using an inbound-gateway responds half the time; using channel-adapters does not. The behavior is the same whether I use a chain or individual channels one after another.

   <bean id = "jacksonMessageConverter" class="org.springframework.amqp.support.converter.Jackson2JsonMessageConverter"/>

   // to be clear, tests use gateway or channel adapters, not both at the same
   // time; just included here for completeness on the example
   <int-amqp:inbound-gateway auto-startup="true"
        request-channel="requestChannel2"
        reply-channel="responseChannel2"
        message-converter="jacksonMessageConverter"
        amqp-template="rpcTemplate"
        queue-names="rpc.KEY"/>

    <int-amqp:inbound-channel-adapter auto-startup="true"
        channel="requestChannel2"
        message-converter="jacksonMessageConverter"
        mapped-request-headers="*"
        concurrent-consumers="5"
        queue-names="rpc.KEY"/>

    <int-amqp:outbound-channel-adapter auto-startup="true"
            mapped-request-headers="*"
            channel="responseChannel2"
            amqp-template="rpcTemplate"/>

    <int:chain input-channel="requestChannel2" output-channel="responseChannel2">
        <int:header-enricher id="jsonContentType">
            <int:header name="Content-Type" value="application/json"/>
        </int:header-enricher>
        <int-http:outbound-gateway id="httpOutbound2"
                                   url="http://api.icndb.com/jokes/{stem}"
                                   http-method="GET"
                                   expected-response-type="java.lang.String">

            <int-http:uri-variable name="stem" expression="payload.stem" />
        </int-http:outbound-gateway>
        <int:json-to-object-transformer
                type="java.util.Map"/>
        <int:transformer>
            <int-script:script lang="groovy">
                <![CDATA[
                    def outputVariables = ['joke':payload.value.joke]
                    return outputVariables
                ]]>
            </int-script:script>
        </int:transformer>
    </int:chain>

    <int:channel id="requestChannel2"/>

    <int:channel id="responseChannel2"/>

Quick and dirty applicate client code in a test:

RabbitTemplate rabbitTemplate = new RabbitTemplate();
rabbitTemplate.setExchange("rpc");
rabbitTemplate.setConnectionFactory(connectionFactory);
rabbitTemplate.setMessageConverter(new Jackson2JsonMessageConverter());
rabbitTemplate.setReplyTimeout((3000L));

Map<String, Object> map = new HashMap<>();
map.put("stem", "random");
int failures = 0;
for (int i=0;i<2;i++) {
    Map<String, Object> result = (Map<String, Object>) rabbitTemplate.convertSendAndReceive("rpc", "YAY", map);
    if (result == null || !result.containsKey("joke")) {
        failures++;
    }
}
assertEquals(0, failures);
  • Have you looked at the [amqp sample](https://github.com/garyrussell/spring-integration-samples/tree/master/basic/amqp) ? You need to show the rest of your configuration and the client-side code; is queue `rpc.KEY` bound to exchange `rpc` with routing key `KEY` ? When using the outbound channel adapter, you shouldn't be using the `amqp_receivedExchange` header, unless the replyTo queue is bound to it with the replyTo. – Gary Russell Jun 06 '17 at 23:18
  • Thank you for the quick response. Edited submission. – user3259437 Jun 07 '17 at 00:24
  • Maybe `rabbitTemplate.setReplyTimeout((3000L));` is not enough for your scenario to wait for reply from the client? – Artem Bilan Jun 07 '17 at 13:43
  • We have Java DSL test with similar `sendAndReceive` approach: https://github.com/spring-projects/spring-integration/blob/master/spring-integration-amqp/src/test/java/org/springframework/integration/amqp/dsl/AmqpTests.java#L100 – Artem Bilan Jun 07 '17 at 13:46
  • The results are the same whether the timeout is 3000 or 30000; exactly half of the responses come back with the input options {"stem":"random"} as the result, and half come back with the proper result from the remote service. – user3259437 Jun 07 '17 at 15:50
  • Following up, the answer seems to be one of timing, in that on some requests the responseChannel has an empty message pulled off its queue. Adding an interceptor whose preReceive casts the MessageChannel to a QueueChannel and checks the queue size and returns false if it is empty solves the issue, but it seems there is probably a cleaner way to handle it. – user3259437 Jun 07 '17 at 21:20
  • Following up to my own comment, there are still some failures when using this preReceive workaround, but they are far less frequent. – user3259437 Jun 07 '17 at 21:41

0 Answers0