2015-03-23 2 views
2

Моя проблема связана с решением, описанным в моей ранее описанной проблеме: Spring integration connecting inbound HTTP gateway with outbound Websocket gateaway. Подводя итог быстро тому, что я пытаюсь сделать, заключается в том, что я хочу передать HTTP-запрос REST, поступающий на мой сервер, другому клиенту websocket, и, получив ответ от клиента websocket, я переношу его в HTTP REST ответ.Весенняя интеграция - Исключение при создании сеанса

Решение, описанное в предыдущей ссылке, работает без проблем. Я попытался немного изменить его, добавив активатор службы lightOnStoringActivator, который создает сеанс, когда я получаю сообщение на входящем шлюзе websocket (см. Ниже мой новый файл конфигурации). После этой модификации у меня есть исключение, говорящее, что полученное сообщение от клиента weboscket не может быть передано на канал ответа. Я уверен, что проблема возникает из строки, которая создает сеанс, потому что если я удалю только строку, создающую сеанс, проблема исчезнет.

Любая идея, почему это происходит, и как ее исправить?

org.springframework.messaging.MessageHandlingException: ; nested exception is org.springframework.messaging.MessageHandlingException: ; nested exceptionan actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still equestContextListener or RequestContextFilter to expose the current request. 
    at org.springframework.integration.websocket.inbound.WebSocketInboundChannelAdapter$1.handleMessage(WebSocketInboundChannelAdapter.java:122) 
    at org.springframework.integration.channel.FixedSubscriberChannel.send(FixedSubscriberChannel.java:70) 
    at org.springframework.integration.channel.FixedSubscriberChannel.send(FixedSubscriberChannel.java:64) 
    at org.springframework.integration.websocket.support.PassThruSubProtocolHandler.handleMessageFromClient(PassThruSubProtocolHandler.java:73) 
    at org.springframework.integration.websocket.inbound.WebSocketInboundChannelAdapter.onMessage(WebSocketInboundChannelAdapter.java:232) 
    at org.springframework.integration.websocket.IntegrationWebSocketContainer$IntegrationWebSocketHandler.handleMessage(IntegrationWebSocketContain 
    at org.springframework.web.socket.handler.WebSocketHandlerDecorator.handleMessage(WebSocketHandlerDecorator.java:75) 
    at org.springframework.web.socket.handler.LoggingWebSocketHandlerDecorator.handleMessage(LoggingWebSocketHandlerDecorator.java:56) 
    at org.springframework.web.socket.handler.ExceptionWebSocketHandlerDecorator.handleMessage(ExceptionWebSocketHandlerDecorator.java:72) 
    at org.springframework.web.socket.adapter.standard.StandardWebSocketHandlerAdapter.handleBinaryMessage(StandardWebSocketHandlerAdapter.java:122) 
    at org.springframework.web.socket.adapter.standard.StandardWebSocketHandlerAdapter.access$100(StandardWebSocketHandlerAdapter.java:42) 
    at org.springframework.web.socket.adapter.standard.StandardWebSocketHandlerAdapter$4.onMessage(StandardWebSocketHandlerAdapter.java:88) 
    at org.springframework.web.socket.adapter.standard.StandardWebSocketHandlerAdapter$4.onMessage(StandardWebSocketHandlerAdapter.java:85) 
    at org.apache.tomcat.websocket.WsFrameBase.sendMessageBinary(WsFrameBase.java:549) 
    at org.apache.tomcat.websocket.WsFrameBase.processDataBinary(WsFrameBase.java:514) 
    at org.apache.tomcat.websocket.WsFrameBase.processData(WsFrameBase.java:274) 
    at org.apache.tomcat.websocket.WsFrameBase.processInputBuffer(WsFrameBase.java:116) 
    at org.apache.tomcat.websocket.server.WsFrameServer.onDataAvailable(WsFrameServer.java:54) 
    at org.apache.tomcat.websocket.server.WsHttpUpgradeHandler$WsReadListener.onDataAvailable(WsHttpUpgradeHandler.java:192) 
    at org.apache.coyote.http11.upgrade.AbstractServletInputStream.onDataAvailable(AbstractServletInputStream.java:178) 
    at org.apache.coyote.http11.upgrade.AbstractProcessor.upgradeDispatch(AbstractProcessor.java:92) 
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:601) 
    at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:310) 
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) 
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) 
    at java.lang.Thread.run(Thread.java:745) 
Caused by: org.springframework.messaging.MessageHandlingException: ; nested exception is java.lang.IllegalStateException: No thread-bound request found:nally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of Disp request. 
    at org.springframework.integration.handler.MethodInvokingMessageProcessor.processMessage(MethodInvokingMessageProcessor.java:78) 
    at org.springframework.integration.handler.ServiceActivatingHandler.handleRequestMessage(ServiceActivatingHandler.java:71) 
    at org.springframework.integration.handler.AbstractReplyProducingMessageHandler.handleMessageInternal(AbstractReplyProducingMessageHandler.java: 
    at org.springframework.integration.handler.AbstractMessageHandler.handleMessage(AbstractMessageHandler.java:78) 
    at org.springframework.integration.dispatcher.AbstractDispatcher.tryOptimizedDispatch(AbstractDispatcher.java:116) 
    at org.springframework.integration.dispatcher.UnicastingDispatcher.doDispatch(UnicastingDispatcher.java:101) 
    at org.springframework.integration.dispatcher.UnicastingDispatcher.dispatch(UnicastingDispatcher.java:97) 
    at org.springframework.integration.channel.AbstractSubscribableChannel.doSend(AbstractSubscribableChannel.java:77) 
    at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:277) 
    at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:239) 
    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:95) 
    at org.springframework.integration.router.AbstractMessageRouter.handleMessageInternal(AbstractMessageRouter.java:164) 
    at org.springframework.integration.handler.AbstractMessageHandler.handleMessage(AbstractMessageHandler.java:78) 
    at org.springframework.integration.dispatcher.AbstractDispatcher.tryOptimizedDispatch(AbstractDispatcher.java:116) 
    at org.springframework.integration.dispatcher.UnicastingDispatcher.doDispatch(UnicastingDispatcher.java:101) 
    at org.springframework.integration.dispatcher.UnicastingDispatcher.dispatch(UnicastingDispatcher.java:97) 
    at org.springframework.integration.channel.AbstractSubscribableChannel.doSend(AbstractSubscribableChannel.java:77) 
    at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:277) 
    at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:239) 
    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:95) 
    at org.springframework.integration.handler.AbstractMessageProducingHandler.sendOutput(AbstractMessageProducingHandler.java:248) 
    at org.springframework.integration.handler.AbstractMessageProducingHandler.produceOutput(AbstractMessageProducingHandler.java:171) 
    at org.springframework.integration.handler.AbstractMessageProducingHandler.sendOutputs(AbstractMessageProducingHandler.java:119) 
    at org.springframework.integration.handler.AbstractReplyProducingMessageHandler.handleMessageInternal(AbstractReplyProducingMessageHandler.java: 
    at org.springframework.integration.handler.AbstractMessageHandler.handleMessage(AbstractMessageHandler.java:78) 
    at org.springframework.integration.dispatcher.AbstractDispatcher.tryOptimizedDispatch(AbstractDispatcher.java:116) 
    at org.springframework.integration.dispatcher.UnicastingDispatcher.doDispatch(UnicastingDispatcher.java:101) 
    at org.springframework.integration.dispatcher.UnicastingDispatcher.dispatch(UnicastingDispatcher.java:97) 
    at org.springframework.integration.channel.AbstractSubscribableChannel.doSend(AbstractSubscribableChannel.java:77) 
    at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:277) 
    at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:239) 
    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:95) 
    at org.springframework.integration.endpoint.MessageProducerSupport.sendMessage(MessageProducerSupport.java:101) 
    at org.springframework.integration.websocket.inbound.WebSocketInboundChannelAdapter.handleMessageAndSend(WebSocketInboundChannelAdapter.java:279 
    at org.springframework.integration.websocket.inbound.WebSocketInboundChannelAdapter.access$000(WebSocketInboundChannelAdapter.java:63) 
    at org.springframework.integration.websocket.inbound.WebSocketInboundChannelAdapter$1.handleMessage(WebSocketInboundChannelAdapter.java:119) 
    ... 25 more 
Caused by: java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or R 
    at org.springframework.web.context.request.RequestContextHolder.currentRequestAttributes(RequestContextHolder.java:131) 
    at com.transacteleurope.service.activator.FingerVeinWebsocketActivator.createHttpSession(FingerVeinWebsocketActivator.java:64) 
    at com.transacteleurope.service.activator.FingerVeinWebsocketActivator.onEnrollmentCaptureForEnrollResponse(FingerVeinWebsocketActivator.java:15 
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) 
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) 
    at java.lang.reflect.Method.invoke(Method.java:483) 
    at org.springframework.expression.spel.support.ReflectiveMethodExecutor.execute(ReflectiveMethodExecutor.java:72) 
    at org.springframework.expression.spel.ast.MethodReference.getValueInternal(MethodReference.java:129) 
    at org.springframework.expression.spel.ast.MethodReference.access$000(MethodReference.java:49) 
    at org.springframework.expression.spel.ast.MethodReference$MethodValueRef.getValue(MethodReference.java:347) 
    at org.springframework.expression.spel.ast.CompoundExpression.getValueInternal(CompoundExpression.java:87) 
    at org.springframework.expression.spel.ast.SpelNodeImpl.getTypedValue(SpelNodeImpl.java:126) 
    at org.springframework.expression.spel.standard.SpelExpression.getValue(SpelExpression.java:327) 
    at org.springframework.integration.util.AbstractExpressionEvaluator.evaluateExpression(AbstractExpressionEvaluator.java:164) 
    at org.springframework.integration.util.MessagingMethodInvokerHelper.processInternal(MessagingMethodInvokerHelper.java:276) 
    at org.springframework.integration.util.MessagingMethodInvokerHelper.process(MessagingMethodInvokerHelper.java:142) 
    at org.springframework.integration.handler.MethodInvokingMessageProcessor.processMessage(MethodInvokingMessageProcessor.java:75) 
    ... 66 more 

Мой конфигурационный файл выглядит следующим образом:

<!-- REST service to turn on the light --> 
<int-http:inbound-gateway 
     supported-methods="POST" 
     request-channel="lightOnRequest" 
     reply-channel="lightOnResponse" 
     path="rest/lighton/{sessionId}"> 
    <int-http:header name="{sessionId}" expression="{sessionId}"/> 
</int-http:inbound-gateway> 

<!-- We add a header SESSION_ID_HEADER to choose the websocket destination client --> 
<int:header-enricher 
    input-channel="lightOnRequest" 
    output-channel="lightOnClientRequest"> 
    <int:header 
     name="#{T(...SimpMessageHeaderAccessor).SESSION_ID_HEADER}" 
     expression="headers.sessionId"/> 
    <int:header-channels-to-string/> 
</int:header-enricher> 

<!-- Websocket out to client --> 
<int-websocket:outbound-channel-adapter 
    channel="lightOnClientRequest" 
    container="serverWebSocketContainer" /> 

<!-- Response reception from the Websocket client --> 
<int-websocket:inbound-channel-adapter 
    channel="lightOnClientResponse" 
    container="serverWebSocketContainer" /> 

<!-- We store some data in the session --> 
<int:service-activator 
    request-channel="lightOnClientResponse" 
    reply-channel="lightOnClientStoredResponse" 
    ref="lightOnStoringActivator" 
    method="onNewRestfullRequest" 
    requires-reply="true" /> 

<!-- The websocket client provides again the reply channel in the headers. 
    The bridge connects the response to the reply channel --> 
<int:bridge input-channel="lightOnClientStoredResponse"/> 
+0

Пожалуйста, отправьте полную трассировку стека; возможно, в Gist или подобном, если он слишком велик, чтобы публиковать здесь. –

+0

Я полностью обновил свой первоначальный пост, потому что файл конфигурации XML на самом деле не так, как я сказал. Фактически, я сделал некоторую очистку, чтобы сделать сообщение как можно меньше. Фактически, происходит то, что я пытаюсь создать сеанс после получения ответа от клиента websocket. Но как я могу сказать, что хочу создать сеанс в запросе REST, используя данные, поступающие от клиента websocket? –

ответ

1

Вам необходимо задержать входящий поток и передавать данные ответа на него.

В настоящий момент ваш входящий поток REST завершается, как только вы отправляете сообщение в адаптер исходящего сетевого адаптера.

Когда ответ приходит на входящий адаптер websocket, вы пытаетесь отправить его обратно на шлюз, но этот контекст уже ушел; кроме того, вы не можете получить доступ к переменным сеанса в «чужом» потоке.

Одно решение было бы сделать lightOnClientRequest в <publish-subscribe-channel/> и подписки на <service-activator/> к ней - убедитесь, что это второй абонент (используйте атрибут order, чтобы убедиться).

В рамках этой службы приостановите поток REST. Затем вместо моста вызовите другой метод службы, который передает данные и освобождает поток. Этот метод должен возвращать void, поэтому поток WS заканчивается в этой точке.

Вы можете использовать Map из LinkedBlockingQueue, используя в качестве ключа заголовок (строку) replyChannel. Имейте шлюз REST thread take() из очереди и получите входящий поток ws put() в очередь. Когда возвращается take(), удалите запись карты.

Остерегайтесь того, что ответ может быть получен до вызова второго потребителя, поэтому вам нужно иметь дело с отсутствующими элементами карты на этой стороне. Или использовать 3 подписчиков на канале

  1. создать запись сопоставления
  2. запись в WS
  3. взять из очереди и удалить запись сопоставления

Вы, вероятно, хотите использовать poll с timeout, а не take() в случае, если вы не получите ответ.

EDIT: (в ответ на ваш комментарий ниже).

Это больше удачи, чем дизайн - после отправки поток http (REST) ​​сидит в шлюзе, ожидая ответа. Он будет работать до тех пор, пока вы не пытаетесь ничего сделать с сеансом HTTP в ответном потоке.

Только HTTP-поток может получить доступ к переменным, зависящим от сеанса. Я не вижу точно, что вы делаете, потому что ваша конфигурация выглядит неполной - например, в трассировке стека есть маршрутизатор, а в вашей конфигурации нет маршрутизатора.

Похоже, что что-то ниже маршрутизатора пытается получить доступ к атрибутам сеанса: currentRequestAttributes. Если вам нужно это сделать, вам нужно извлечь переменные из сеанса в основном потоке и сохранить их в заголовке. Вы просто не можете получить доступ к контексту HTTP-запроса из потока ответов WebSocket.

+0

Привет Гэри. Спасибо за Ваш ответ. Я согласен с вами в том, что ваше решение, вероятно, будет работать, хотя мне кажется немного сложнее. Я не понимаю, что если я удалю код создания сеанса, исключение не появится, а это значит, что поток websocket смог совместить поток REST, просто используя 'ReplyChannel'. Поэтому мне все еще интересно, есть ли более простое решение. –

+0

Мой комментарий слишком длинный, поэтому я отредактировал ответ. –

Смежные вопросы