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);