Интеграция Spring с Citrus: нет доступных заголовков output-channel или replyChannel

Я настроил тест, в котором я сначала отправляю «Req» с сервера в свое приложение, а затем отвечаю серверу «Rsp». Я успешно получаю сообщение запроса, но затем получаю следующую ошибку при попытке ответить:

org.springframework.messaging.MessagingException: Dispatcher failed to deliver Message; nested exception is org.springframework.messaging.core.DestinationResolutionException: no output-channel or replyChannel header available
at org.springframework.integration.dispatcher.AbstractDispatcher.wrapExceptionIfNecessary(AbstractDispatcher.java:133)
at org.springframework.integration.dispatcher.AbstractDispatcher.tryOptimizedDispatch(AbstractDispatcher.java:120)
at org.springframework.integration.dispatcher.UnicastingDispatcher.doDispatch(UnicastingDispatcher.java:147)
at org.springframework.integration.dispatcher.UnicastingDispatcher.dispatch(UnicastingDispatcher.java:120)
at org.springframework.integration.channel.AbstractSubscribableChannel.doSend(AbstractSubscribableChannel.java:77)
at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:442)
at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:392)
at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:115)
at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:45)
at org.springframework.messaging.core.AbstractMessageSendingTemplate.send(AbstractMessageSendingTemplate.java:105)
at org.springframework.integration.handler.AbstractMessageProducingHandler.sendOutput(AbstractMessageProducingHandler.java:231)
at org.springframework.integration.handler.AbstractMessageProducingHandler.produceOutput(AbstractMessageProducingHandler.java:154)
at org.springframework.integration.handler.AbstractMessageProducingHandler.sendOutputs(AbstractMessageProducingHandler.java:102)
at org.springframework.integration.handler.AbstractReplyProducingMessageHandler.handleMessageInternal(AbstractReplyProducingMessageHandler.java:105)
at org.springframework.integration.handler.AbstractMessageHandler.handleMessage(AbstractMessageHandler.java:127)
at org.springframework.integration.dispatcher.AbstractDispatcher.tryOptimizedDispatch(AbstractDispatcher.java:116)
at org.springframework.integration.dispatcher.UnicastingDispatcher.doDispatch(UnicastingDispatcher.java:147)
at org.springframework.integration.dispatcher.UnicastingDispatcher.dispatch(UnicastingDispatcher.java:120)
at org.springframework.integration.channel.AbstractSubscribableChannel.doSend(AbstractSubscribableChannel.java:77)
at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:442)
at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:392)
at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:115)
at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:45)
at org.springframework.messaging.core.AbstractMessageSendingTemplate.send(AbstractMessageSendingTemplate.java:105)
at com.consol.citrus.channel.ChannelProducer.send(ChannelProducer.java:66)
at com.consol.citrus.actions.SendMessageAction.doExecute(SendMessageAction.java:103)
at com.consol.citrus.actions.AbstractTestAction.execute(AbstractTestAction.java:42)
at com.consol.citrus.TestCase.executeAction(TestCase.java:211)
... 31 more
Caused by: org.springframework.messaging.core.DestinationResolutionException: no output-channel or replyChannel header available
at org.springframework.integration.handler.AbstractMessageProducingHandler.sendOutput(AbstractMessageProducingHandler.java:226)
at org.springframework.integration.handler.AbstractMessageProducingHandler.produceOutput(AbstractMessageProducingHandler.java:154)
at org.springframework.integration.handler.AbstractMessageProducingHandler.sendOutputs(AbstractMessageProducingHandler.java:102)
at org.springframework.integration.handler.AbstractReplyProducingMessageHandler.handleMessageInternal(AbstractReplyProducingMessageHandler.java:105)
at org.springframework.integration.handler.AbstractMessageHandler.handleMessage(AbstractMessageHandler.java:127)
at org.springframework.integration.dispatcher.AbstractDispatcher.tryOptimizedDispatch(AbstractDispatcher.java:116)
... 57 more

Пожалуйста, смотрите мою конфигурацию bean-компонента ниже: citrus-config.xml

<citrus:channel-endpoint id="citrusServiceReqEndpoint"
                         channel-name="req_transformed" />

<citrus:channel-endpoint id="citrusServiceRspEndpoint"
                         channel-name="rsp" />


<int-ip:tcp-connection-factory id="client"
                               type="client" host="localhost" port="12345" single-use="false"
                               so-timeout="10000" using-nio="true" deserializer="javaSerializer"
                               serializer="javaSerializer" />

<bean id="javaSerializer"
      class="org.springframework.integration.ip.tcp.serializer.ByteArrayLfSerializer" />

<int:channel id="req" />

<int:channel id="req_transformed">
    <int:queue />
</int:channel>

<int:channel id="rsp">
</int:channel>

<int:channel id="rsp_transformed">
</int:channel>

<int:object-to-string-transformer id="incomingTransformer" input-channel="req" output-channel="req_transformed" />

<int:object-to-string-transformer id="outgoingTransformer" input-channel="rsp" output-channel="rsp_transformed" />

<int-ip:tcp-inbound-gateway id="gateway"
                            connection-factory="client"
                            request-channel="req"
                            reply-channel="rsp_transformed"
                            client-mode="true" />

Test case:

receive("citrusServiceReqEndpoint").messageType(MessageType.PLAINTEXT).payload("Req");
        send("citrusServiceRspEndpoint").payload("Rsp");
        receive("citrusServiceReqEndpoint").messageType(MessageType.PLAINTEXT).payload("Req2");
        send("citrusServiceRspEndpoint").payload("Rsp2");

По сути, мои намерения состоят в том, чтобы получить данные по каналу «req», затем преобразовать байты в строку на канал req_transformed, а затем проверить, что строка равна «Req». Затем я хочу отправить обратно строку «Rsp», которая преобразуется в байты, а затем помещается в канал rsp_transformed в качестве ответа.

Пожалуйста, помогите мне понять, где проблема. Обратите внимание, что когда я заменяю tcp-inbound-gateway двумя адаптерами канала:

<int-ip:tcp-outbound-channel-adapter
            id="outboundClient" channel="rsp" connection-factory="client" />

<int-ip:tcp-inbound-channel-adapter
            id="inboundClient" channel="req" connection-factory="client" client-mode="true" />

тест-кейс работает на 100%. Почему тогда он не будет работать со шлюзом?


person mdewit    schedule 16.05.2016    source источник


Ответы (2)


Ваш tcp-inbound-gateway работает с синхронным обменом сообщениями. Поэтому вам необходимо использовать конечную точку синхронного канала в Citrus соответственно.

<citrus:channel-sync-endpoint id="citrusServiceSyncEndpoint"
                     channel-name="req_transformed"/>

Синхронная конечная точка позаботится о специальных заголовках, которые связывают каналы req/resp с запросом/ответом. Итак, ваш тест выглядит так:

receive("citrusServiceSyncEndpoint")
    .messageType(MessageType.PLAINTEXT)
    .payload("Req");
send("citrusServiceSyncEndpoint")
    .messageType(MessageType.PLAINTEXT)
    .payload("Rsp");

Обратите внимание, что одна и та же синхронная конечная точка используется как для запроса, так и для ответа.

person Christoph Deppisch    schedule 23.05.2016
comment
Ух ты, оба автора соответствующих фреймворков ответили на мой вопрос и помогли мне его решить! Большое спасибо, добрые господа :) - person mdewit; 23.05.2016

Я не знаком с цитрусовыми, но при использовании шлюза ответ должен содержать определенные заголовки из запроса - в частности, заголовок replyChannel в этом случае - так шлюз узнает, для какого запроса предназначен ответ (может быть много одновременных запросов через шлюз).

Вы можете использовать пару взаимодействующих адаптеров входящего/исходящего канала TCP вместо входящего шлюза, но тогда вам все равно нужно сообщить исходящему адаптеру, для какого сокета предназначен ответ, путем распространения заголовка ip_connectionId из запроса в ответ.

person Gary Russell    schedule 16.05.2016
comment
Хорошо, спасибо, я думаю, это поможет мне немного. Вероятно, мне придется поместить какой-то компонент перед Citrus в конвейер, который будет хранить полученный заголовок, а затем заполнять ответ этим значением после его отправки. - person mdewit; 16.05.2016