I'm on a request/reply scenario and I receive requests that I need to reply through the same input connection. To give a response I need to do a websocket request to another system. Right now I'm connecting an output-channel to the reply channel of the gateway this way (I'm using a transformer to adapt the incoming messages):
<int:channel id="emrOutboundMessageTransformedChannel"/>
<int:channel id="emrInboundMessageTransformerChannel"/>
<int:channel id="emrOutboundChannel"/>
<int:channel id="cloudOutboundChannel"/>
<!-- Server WebSocket -->
<beans:bean id="webSocketClient"
class="org.springframework.web.socket.client.standard.StandardWebSocketClient"/>
<int-websocket:client-container id="webSocketClientContainer"
client="webSocketClient"
uri="ws://${cloud.webSocket.host}:${cloud.webSocket.port}"/>
<!-- Client Inbound -->
<int:gateway id="emrInboundGateway"
service-interface="com.roche.iconnect.emr.core.EmrMessageInjector">
<int:method name="injectMessage" request-channel="emrInboundMessageTransformerChannel" reply-channel="emrOutboundMessageTransformedChannel"/>
</int:gateway>
<int:transformer
id="emrInboundTransformer"
input-channel="emrInboundMessageTransformerChannel"
output-channel="cloudOutboundChannel"
method="convertEmrMessageToGenericMessage">
<beans:bean class="com.roche.iconnect.emr.EmrMessageAdapter"/>
</int:transformer>
<!-- Client Outbound -->
<int:transformer
id="emrOutboundTransformer"
input-channel="emrOutboundChannel"
output-channel="emrOutboundMessageTransformedChannel"
method="convertGenericMessageToEmrMessage">
<beans:bean class="com.roche.iconnect.emr.EmrMessageAdapter"/>
</int:transformer>
<!-- Server Inbound -->
<int-websocket:inbound-channel-adapter id="webSocketClientInboundAdapter"
container="webSocketClientContainer"
channel="emrOutboundChannel"/>
<int-websocket:outbound-channel-adapter id="webSocketClientOutboundAdapter"
container="webSocketClientContainer"
channel="cloudOutboundChannel"/>
It basically is chanining componenets like this:
CLIENT -> gateway -> transformer -> wsClient -> SERVER -> wsClient -> transformer -> gateway-reply
The problem here is that I get an error:
org.springframework.messaging.core.DestinationResolutionException: no output-channel or replyChannel header available
Websockets are by nature bidirectional so I can get any message without sending anything, is not a real request/reply channel but I need to use it this way. The websocket server only sends "reply" messages to previous "request" messages.
I need to get through the return of the gateway the response of the websocket.
Update 1
I added a header enricher after the gateway to stringify and store into the registry the reply and error channels:
(Added "replyChannelRegistry" channel)
<int:gateway id="emrInboundGateway"
service-interface="com.roche.iconnect.emr.core.EmrMessageInjector">
<int:method name="injectMessage" request-channel="replyChannelRegistry" reply-channel="emrOutboundMessageTransformedChannel"/>
</int:gateway>
<beans:bean id="integrationHeaderChannelRegistry"
class="org.springframework.integration.channel.DefaultHeaderChannelRegistry">
<beans:constructor-arg index="0" value="${config.timeout}"/>
</beans:bean>
<int:header-enricher input-channel="replyChannelRegistry" output-channel="emrInboundMessageTransformerChannel">
<int:header-channels-to-string time-to-live-expression="${config.timeout}"/>
<int:correlation-id value="CORRELATION_ID_TEST"/>
</int:header-enricher>
Now the question is... How can I retrieve these reply and error channels? Inside the messages that go through the system I have a unique Id that I can add to the header, but how can I get the reply and error channels from this correlation Id that will come from the websocket?
Update 2 (SOLVED)
I guess that this scenario is pretty usual. A request arrives to you API and to respond to this request you must get the information from another system over a channel that breaks the request/reply pattern.
Somewhere you need to store a mapping between a unique id that you need to extract from the request and the channel from where it arrives. This unique id needs to be recreated by using the data that you'll get from the other system, otherwise you cannot recover the original channel.
Steps are:
- Request comes.
- Use the
reply-channel
property of thegateway
component. - Stringify the replyChannel by using the
header-enricher
component (see Update 1). You cannot have aMap<String, MessageChannel>
mapping (id/channel), you need to stringify the channel. - Ask for the response data.
- Get the response data.
- Now you need to send the reply through the same original connection.
To do so, use another
header-enricher
component. The idea is to set thereplyChannel
of the message that will return to the gateway (ensure that the class of the message matches the return class of the gateway) and Spring automatically will link the "temporal internal point-to-point" (the one that we stringified previously) to thereply-channel
of the gateway. Magic!<int:header-enricher input-channel="recoverOriginalChannel" output-channel="gatewayReplyChannel"> <int:reply-channel ref="yourBean" method="enrichReplyChannelHeader" /> </int:header-enricher>
Done!