I have the following requirement for my application:
I have an integration flow which takes files from a directory via
Files.inboundAdapter
and a polling configuration as follows:
#Bean public PollerSpec orderOutboundFlowTempFileInPoller() {
return Pollers
.fixedDelay(pollerDelay)
.maxMessagesPerPoll(100)
.transactional();
}
The files should be transferred to a remote host via RemoteFileTemplate. The application runs in a docker container which should be stoppable for maintainance or rollout purposes.
When the container is shutdown, the flow should finish writing the file to the remote host and should not accept new incoming files.
Therefore I have implemented a graceful shutdown as follows:
#Override public void onApplicationEvent(final ContextClosedEvent event) {
LOG.info("Trying to gracefully shutdown App");
//CHECKSTYLE:OFF
allFlowPollers.forEach(
p -> {
try {
p.destroy();
} catch (final Exception e) {
LOG.warn("Unable to destroy poller.");
}
}
);
//CHECKSTLYE:ON
FLOWS_TO_SHUTDOWN.forEach(GracefulShutdownAware::shutdown);
}
I assumed when I destroy the pollers, no further file would be read from source. The RemoteFileTemplate does send the current file correctly, there is no problem.
But the poller still seems to get new files and when the application is nearly shutdown, an exception appears as follows:
timestamp=15:55:56.599, thread=task-scheduler-2, severity=ERROR, class=o.s.i.h.LoggingHandler, message=org.springframework.messaging.MessageDeliveryException: Dispatcher has no subscribers for channel 'application.flowTempFileIn.channel#0'.; nested exception is org.springframework.integration.MessageDispatchingException: Dispatcher has no subscribers, failedMessage=GenericMessage [payload=/servicedata/tmp/1544088550162_57280.xml, headers={file_originalFile=/servicedata/tmp/1544088550162_57280.xml, id=d04473ff-d1bd-173e-d801-b7b9fd31596c, file_name=1544088550162_57280.xml, file_relativePath=1544088550162_57280.xml, timestamp=1544108156591}], failedMessage=GenericMessage [payload=/servicedata/tmp/1544088550162_57280.xml, headers={file_originalFile=/servicedata/tmp/1544088550162_57280.xml, id=d04473ff-d1bd-173e-d801-b7b9fd31596c, file_name=1544088550162_57280.xml, file_relativePath=1544088550162_57280.xml, timestamp=1544108156591}]
at org.springframework.integration.channel.AbstractSubscribableChannel.doSend(AbstractSubscribableChannel.java:77)
at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:445)
at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:394)
at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:181)
at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:160)
at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:47)
at org.springframework.messaging.core.AbstractMessageSendingTemplate.send(AbstractMessageSendingTemplate.java:108)
at org.springframework.integration.endpoint.SourcePollingChannelAdapter.handleMessage(SourcePollingChannelAdapter.java:227)
at org.springframework.integration.endpoint.AbstractPollingEndpoint.doPoll(AbstractPollingEndpoint.java:290)
at sun.reflect.GeneratedMethodAccessor292.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:343)
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:197)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:294)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:98)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212)
at com.sun.proxy.$Proxy138.call(Unknown Source)
at org.springframework.integration.endpoint.AbstractPollingEndpoint$Poller.lambda$run$0(AbstractPollingEndpoint.java:391)
at org.springframework.integration.util.ErrorHandlingTaskExecutor.lambda$execute$0(ErrorHandlingTaskExecutor.java:57)
at org.springframework.core.task.SyncTaskExecutor.execute(SyncTaskExecutor.java:50)
at org.springframework.integration.util.ErrorHandlingTaskExecutor.execute(ErrorHandlingTaskExecutor.java:55)
at org.springframework.integration.endpoint.AbstractPollingEndpoint$Poller.run(AbstractPollingEndpoint.java:385)
at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54)
at org.springframework.scheduling.concurrent.ReschedulingRunnable.run(ReschedulingRunnable.java:93)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293)
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:748)
Caused by: org.springframework.integration.MessageDispatchingException: Dispatcher has no subscribers, failedMessage=GenericMessage [payload=/servicedata/tmp/1544088550162_57280.xml, headers={file_originalFile=/servicedata/tmp/1544088550162_57280.xml, id=d04473ff-d1bd-173e-d801-b7b9fd31596c, file_name=1544088550162_57280.xml, file_relativePath=1544088550162_57280.xml, timestamp=1544108156591}]
at org.springframework.integration.dispatcher.UnicastingDispatcher.doDispatch(UnicastingDispatcher.java:138)
at org.springframework.integration.dispatcher.UnicastingDispatcher.dispatch(UnicastingDispatcher.java:105)
at org.springframework.integration.channel.AbstractSubscribableChannel.doSend(AbstractSubscribableChannel.java:73)
... 33 more
Is there any other way, I can achieve this requirement? Which is: stopping the application should let the current file finish, not accept further files to be read and still closing without any weird exceptions?
What I assume it's some kind of timing issue since the integration flow and the reaction to the ContextClosedEvent are running on different Threads. The poller isn't destroyed completely but the transform-subscriber of the outbound channel is already destroyed.
I also tried to stop the poller via control bus, but the outcome was the same.
Thanks in advance :)
The problem is not about poller, but InboundChannelAdapter. Would be great to see your IntegrationFlow definition. Actually it must stop during application context shutdown. And you don't need to do anything else from your side.
The point is that all active Spring Integration components implement SmartLifecycle and they are stop()'ed gracefully during appropriate application context close phase.
Related
Trying to follow the link to add azure event into logstash, I have the below issue:
[2020-02-13T14:06:28,886][ERROR][com.microsoft.azure.eventprocessorhost.PartitionManager] host logstash-5fdbcee8-e368-44de-bc13-c640a36f646f: Exception while initializing stores, not starting partition manager com.microsoft.azure.eventhubs.IllegalEntityException: Failure getting partition ids for event hub
at com.microsoft.azure.eventprocessorhost.PartitionManager.lambda$cachePartitionIds$4(PartitionManager.java:80) ~[azure-eventhubs-eph-2.1.0.jar:?]
at java.util.concurrent.CompletableFuture.uniHandle(CompletableFuture.java:836) ~[?:1.8.0_242]
at java.util.concurrent.CompletableFuture$UniHandle.tryFire(CompletableFuture.java:811) ~[?:1.8.0_242]
at java.util.concurrent.CompletableFuture$Completion.run(CompletableFuture.java:456) [?:1.8.0_242]
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) [?:1.8.0_242]
at java.util.concurrent.FutureTask.run(FutureTask.java:266) [?:1.8.0_242]
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180) [?:1.8.0_242]
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293) [?:1.8.0_242]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [?:1.8.0_242]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [?:1.8.0_242]
Can someone help ?
I got the hint from this question. It appears the consumer SAS policy still needs manage privileges.
My problem is related to the solution described in my previously described issue : Spring integration connecting inbound HTTP gateway with outbound Websocket gateaway. To sum-up quickly what I'm trying to do, is that I want to transfer a HTTP REST request coming to my server, to another websocket client, and when receiving the answer from the websocket client, I transfer it to the HTTP REST response.
The solution described in the previous link works without any problem. I tried to modify it a little bit, by adding a service activator lightOnStoringActivator that creates a session, whenever I receive a message on the inbound websocket gateway (see below my new configuration file). After this modification, I have an exception, saying that the received message from the weboscket client can't be transfered to the reply channel. I'm sure the problem is coming from the line that is creating the session, because if I remove only the line creating the session, the problem disappears.
Any idea why this happens, and how to fix it ?
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
My configuration file is the following:
<!-- 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"/>
You need to hold up the inbound thread and transfer the reply data to it.
Currently, your inbound REST flow ends as soon as you send the message to the outbound websocket adapter.
When the reply comes in on the websocket inbound adapter, you're trying to send it back to the gateway but that context is already gone; further, you can't access session variables on a "foreign" thread.
One solution would be to make the lightOnClientRequest a <publish-subscribe-channel/> and subscribe a <service-activator/> to it - make sure it's the second subscriber (use the order attribute to be sure).
Within that service, suspend the REST thread. Then, instead of the bridge, invoke another method on the service that transfers the data and releases the thread. That method should return void, so the WS flow ends at that point.
You could use a Map of LinkedBlockingQueue using the replyChannel header (string) as the key. Have the gateway REST thread take() from the queue and have the ws inbound thread put() to the queue. When the take() returns, remove the map entry.
Beware that the reply may be received before the second consumer is invoked so you need to deal with missing map entries on that side. Or, use 3 subscribers on the channel
create the map entry
write to WS
take from the queue and remove the map entry
You probably want to use poll with a timeout rather than take() in case you don't get a reply.
EDIT: (in response to your comment below).
That is more by luck than design - after the send, the http (REST) thread is sitting in the gateway awaiting the reply. It will work as long as you don't attempt to do anything with the HTTP session on the replying thread.
Only the http thread can access session-scoped variables. I can't see exactly what you are doing because your configuration appears incomplete - for example, there is a router in the stack trace and your configuration shows no router.
It appears that something downstream of the router is trying to access the session attributes: currentRequestAttributes. If you need to do that, you need to extract the variables from the session on the main thread and store then in a header. You simply cannot access the HTTP request context from the WebSocket reply thread.
I have a spring-integration 4.0.x based application which makes connections to a Websphere MQ manager for producing/consuming JMS messages. The application is currently deployed on a Tomcat 7.0.x container which, when running on a Windows host, the application stops as expected when the container is shutdown but, when running on a RH6 Linux host, the application fails to stop as expected when the container is shutdown.
Looking at trace level logging during the Linux based shutdown, I can see that the application gets stuck when trying to close JMS connections to the Websphere MQ queues:
15:24:30.628 [localhost-startStop-2] DEBUG org.springframework.beans.factory.support.DisposableBeanAdapter - Invoking destroy() on bean with name 'cachingConnectionFactory'
15:24:30.628 [localhost-startStop-2] DEBUG org.springframework.jms.connection.CachingConnectionFactory - Closing shared JMS Connection: com.ibm.mq.jms.MQQueueConnection#13909638
The application then remains at this point for about 15 minutes until the following then gets logged:
15:40:24.247 [task-scheduler-1] TRACE org.springframework.jms.connection.CachingConnectionFactory - Logical close of cached JMS Session failed - discarding it
javax.jms.IllegalStateException: MQJMS1024: session closed
at com.ibm.mq.jms.MQSession.checkSessionOpen(MQSession.java:2629) ~[com.ibm.mqjms-6.0.2.3.jar:6.0.2.3 - j600-203-080121 ]
at com.ibm.mq.jms.MQSession.getTransacted(MQSession.java:1385) ~[com.ibm.mqjms-6.0.2.3.jar:6.0.2.3 - j600-203-080121 ]
at org.springframework.jms.connection.CachingConnectionFactory$CachedSessionInvocationHandler.logicalClose(CachingConnectionFactory.java:457) ~[spring-jms-4.0.7.RELEASE.jar:4.0.7.RELEASE]
at org.springframework.jms.connection.CachingConnectionFactory$CachedSessionInvocationHandler.invoke(CachingConnectionFactory.java:309) [spring-jms-4.0.7.RELEASE.jar:4.0.7.RELEASE]
at com.sun.proxy.$Proxy92.close(Unknown Source) [?:?]
at org.springframework.jms.support.JmsUtils.closeSession(JmsUtils.java:108) [spring-jms-4.0.7.RELEASE.jar:4.0.7.RELEASE]
at org.springframework.jms.core.JmsTemplate.execute(JmsTemplate.java:497) [spring-jms-4.0.7.RELEASE.jar:4.0.7.RELEASE]
at org.springframework.jms.core.JmsTemplate.receiveSelected(JmsTemplate.java:761) [spring-jms-4.0.7.RELEASE.jar:4.0.7.RELEASE]
at org.springframework.integration.jms.JmsDestinationPollingSource.doReceiveJmsMessage(JmsDestinationPollingSource.java:122) [spring-integration-jms-4.0.4.RELEASE.jar:?]
at org.springframework.integration.jms.JmsDestinationPollingSource.receive(JmsDestinationPollingSource.java:96) [spring-integration-jms-4.0.4.RELEASE.jar:?]
at org.springframework.integration.endpoint.SourcePollingChannelAdapter.receiveMessage(SourcePollingChannelAdapter.java:144) [spring-integration-core-4.0.6.RELEASE.jar:?]
at org.springframework.integration.endpoint.AbstractPollingEndpoint.doPoll(AbstractPollingEndpoint.java:192) [spring-integration-core-4.0.6.RELEASE.jar:?]
at org.springframework.integration.endpoint.AbstractPollingEndpoint.access$000(AbstractPollingEndpoint.java:55) [spring-integration-core-4.0.6.RELEASE.jar:?]
at org.springframework.integration.endpoint.AbstractPollingEndpoint$1.call(AbstractPollingEndpoint.java:149) [spring-integration-core-4.0.6.RELEASE.jar:?]
at org.springframework.integration.endpoint.AbstractPollingEndpoint$1.call(AbstractPollingEndpoint.java:146) [spring-integration-core-4.0.6.RELEASE.jar:?]
at org.springframework.integration.endpoint.AbstractPollingEndpoint$Poller$1.run(AbstractPollingEndpoint.java:298) [spring-integration-core-4.0.6.RELEASE.jar:?]
at org.springframework.integration.util.ErrorHandlingTaskExecutor$1.run(ErrorHandlingTaskExecutor.java:52) [spring-integration-core-4.0.6.RELEASE.jar:?]
at org.springframework.core.task.SyncTaskExecutor.execute(SyncTaskExecutor.java:50) [spring-core-4.0.8.RELEASE.jar:4.0.8.RELEASE]
at org.springframework.integration.util.ErrorHandlingTaskExecutor.execute(ErrorHandlingTaskExecutor.java:49) [spring-integration-core-4.0.6.RELEASE.jar:?]
at org.springframework.integration.endpoint.AbstractPollingEndpoint$Poller.run(AbstractPollingEndpoint.java:292) [spring-integration-core-4.0.6.RELEASE.jar:?]
at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54) [spring-context-4.0.8.RELEASE.jar:4.0.8.RELEASE]
at org.springframework.scheduling.concurrent.ReschedulingRunnable.run(ReschedulingRunnable.java:81) [spring-context-4.0.8.RELEASE.jar:4.0.8.RELEASE]
at java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source) [?:1.8.0_25]
at java.util.concurrent.FutureTask.run(Unknown Source) [?:1.8.0_25]
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(Unknown Source) [?:1.8.0_25]
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(Unknown Source) [?:1.8.0_25]
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source) [?:1.8.0_25]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source) [?:1.8.0_25]
at java.lang.Thread.run(Unknown Source) [?:1.8.0_25]
15:40:24.257 [task-scheduler-1] DEBUG org.springframework.jms.connection.CachingConnectionFactory - Closing cached Session: com.ibm.mq.jms.MQQueueSession#6d33d604
15:40:24.258 [task-scheduler-1] DEBUG org.springframework.integration.endpoint.SourcePollingChannelAdapter - Poll interrupted - during stop()? : MQJMS1024: session closed; nested exception is javax.jms.IllegalStateException: MQJMS1024: session closed
My application runs using the following stack:
java 8
com.ibm:com.ibm.mq:6.0.2.3
com.ibm:com.ibm.mqjms:6.0.2.3
javax.jms:jms:1.1
org.springframework.integration:spring-integration-core:4.0.6.RELEASE
org.springframework.integration:spring-integration-java-dsl:1.0.1.RELEASE
org.springframework.integration:spring-integration-jms:4.0.4.RELEASE
My application's JMS config is shown here:
#Configuration
#ComponentScan
public class JmsConfig {
#Autowired
private Properties jndiProperties;
private ConnectionFactory mqConnectionFactory() throws NamingException {
Context ctx = new InitialContext(jndiProperties);
try {
MQQueueConnectionFactory connectionFactory = (MQQueueConnectionFactory)
ctx.lookup("jms/service/SERVICE_QCF");
return connectionFactory;
} finally {
ctx.close();
}
}
#Bean
public CachingConnectionFactory cachingConnectionFactory() throws NamingException {
CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
connectionFactory.setTargetConnectionFactory(mqConnectionFactory());
connectionFactory.setCacheConsumers(false);
return connectionFactory;
}
}
My application's Integration config is partially shown here:
#Configuration
#EnableIntegration
public class IntegrationConfig {
...
#Autowired
private CachingConnectionFactory cachingConnectionFactory;
#Bean
public IntegrationFlow requestFlow() {
return IntegrationFlows
.from("request.ch")
.handle(Jms.outboundAdapter(cachingConnectionFactory)
.destination("REQUEST_QUEUE")).get();
}
#Bean
public IntegrationFlow responseFlow() {
return IntegrationFlows
.from(Jms.inboundAdapter(cachingConnectionFactory).destination(
"RESPONSE_QUEUE"),
c -> c.poller(Pollers.fixedRate(1000).maxMessagesPerPoll(10)))
.channel("response.ch")
.get();
}
...
}
How can I ensure that connections to the MQ manager's queues are properly closed upon the application stopping/closing down?
Thanks in advance,
PM
Reposting my comment as an answer...
You're using an incredibly old version of the MQ Java client, 6.0.2.3 - which isn't even supported by IBM anymore. Have you considered using something newer like the 7.5.0.5 client? Much more up to date, with many fixes and enhancements in. Also worth noting that Java 8 isn't supported by any version of MQ Java client either, newest which is supported is Java 7.
IBM supply their 7.5 clients free of charge here: http://www-01.ibm.com/support/docview.wss?uid=swg24032744
The design of MQ is such that all versions of client can work with all versions of queue manager. So it certainly isn't a problem to use a 7.5 client with a 6.0 queue manager, or any other combination.
I'm sure IBM will announce support for Java 8 with IBM MQ at some point, but there's nothing public I'm aware of with any dates. You can request support (and functional enhancements) here: https://www.ibm.com/developerworks/rfe/
Might be worth submitting an RFE for Java 8 on IBM MQ in your case. (Note: it's still called WebSphere MQ on the RFE pages)
I've encountered a similar problem stopping Camel route consuming IBM MQ with Spring CachingConnectionFactory in OSGi container (Karaf).
The solution was setting:
cachingConnectionFactory.setCacheConsumers(false);
If that doesn't help, try to call:
cachingConnectionFactory.resetConnection();
from some kind of your application's destroying method.
I have a Spring Integration application which is using Solace as the message broker. The application is working - messages are received on queues and then correctly placed on topics - yet the following error message is being thrown when a message is created. Am trying to figure out why this is occurring.
03:31:12.479 [taskExecutor-8] WARN o.s.i.jms.DefaultJmsHeaderMapper - failed to map Message header 'JMS_Solace_TopicSequenceNumber' to JMS property
java.lang.IllegalArgumentException: Property SOLACE_JMS_PROP_TOPIC_SEQUENCE_NUMBER is not settable on a message
at com.solacesystems.jms.message.SolMessage.setObjectProperty(SolMessage.java:486) ~[sol-jms-6.0.0.146.jar:na]
at org.springframework.integration.jms.DefaultJmsHeaderMapper.fromHeaders(DefaultJmsHeaderMapper.java:131) [spring-integration-jms-2.2.0.RC2.jar:na]
at org.springframework.integration.jms.DefaultJmsHeaderMapper.fromHeaders(DefaultJmsHeaderMapper.java:53) [spring-integration-jms-2.2.0.RC2.jar:na]
at org.springframework.integration.jms.JmsSendingMessageHandler$HeaderMappingMessagePostProcessor.postProcessMessage(JmsSendingMessageHandler.java:162) [spring-integration-jms-2.2.0.RC2.jar:na]
at org.springframework.jms.core.JmsTemplate$8.createMessage(JmsTemplate.java:656) [spring-jms-3.1.2.RELEASE.jar:3.1.2.RELEASE]
at org.springframework.jms.core.JmsTemplate.doSend(JmsTemplate.java:565) [spring-jms-3.1.2.RELEASE.jar:3.1.2.RELEASE]
at org.springframework.jms.core.JmsTemplate$4.doInJms(JmsTemplate.java:546) [spring-jms-3.1.2.RELEASE.jar:3.1.2.RELEASE]
at org.springframework.jms.core.JmsTemplate.execute(JmsTemplate.java:466) [spring-jms-3.1.2.RELEASE.jar:3.1.2.RELEASE]
at org.springframework.jms.core.JmsTemplate.send(JmsTemplate.java:543) [spring-jms-3.1.2.RELEASE.jar:3.1.2.RELEASE]
at org.springframework.jms.core.JmsTemplate.convertAndSend(JmsTemplate.java:653) [spring-jms-3.1.2.RELEASE.jar:3.1.2.RELEASE]
at org.springframework.integration.jms.JmsSendingMessageHandler.send(JmsSendingMessageHandler.java:142) [spring-integration-jms-2.2.0.RC2.jar:na]
at org.springframework.integration.jms.JmsSendingMessageHandler.handleMessageInternal(JmsSendingMessageHandler.java:112) [spring-integration-jms-2.2.0.RC2.jar:na]
at org.springframework.integration.handler.AbstractMessageHandler.handleMessage(AbstractMessageHandler.java:73) [spring-integration-core-2.2.0.RC2.jar:na]
at org.springframework.integration.dispatcher.UnicastingDispatcher.doDispatch(UnicastingDispatcher.java:115) [spring-integration-core-2.2.0.RC2.jar:na]
at org.springframework.integration.dispatcher.UnicastingDispatcher.access$000(UnicastingDispatcher.java:52) [spring-integration-core-2.2.0.RC2.jar:na]
at org.springframework.integration.dispatcher.UnicastingDispatcher$1.run(UnicastingDispatcher.java:97) [spring-integration-core-2.2.0.RC2.jar:na]
at org.springframework.integration.util.ErrorHandlingTaskExecutor$1.run(ErrorHandlingTaskExecutor.java:52) [spring-integration-core-2.2.0.RC2.jar:na]
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(Unknown Source) [na:1.6.0_06]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source) [na:1.6.0_06]
at java.lang.Thread.run(Unknown Source) [na:1.6.0_06]
Any help is greatly appreciated
I am not familiar with SOLACE, but it sounds like there is a read-only property on the inbound message. By default, all properties are made available as SI headers and, similarly, we try to map to SI headers to JMS properties (if they are simple types).
This is just a WARNing - you can
Ignore it - set the log level for the mapper to ERROR
Use a custom header mapper to skip it
Set an outboundPrefix on the DefaultJmsHeaderMapper so that the property gets a different name
Add a <header-filter/> before the outbound adapter to remove it
I have a Grails 2.0.1 app and I spawn worker threads. My worker threads use GORM to read/write domain objects from MySql. Given that the code that is accessing the DB is not inside of an HTTP request, I create a Hibernate session for the save() like this:
MyClass.withTransaction { status ->
myClass.save()
}
I do that to resolve the "No Hibernate Session bound to thread" problem.
This worked fine on Grails 1.3.7. I am currently attempting to upgrade to Grails 2.0.1, and am seeing an exception I do not understand.
The following stack trace shows the exception.
Although I recognize the exception, and normally resolve it via the "withTransaction" technique shown above, the thing that baffles me about this one is that it is happening on a worker thread that I did not create.
Exception in thread "pool-12-thread-2" org.hibernate.HibernateException: No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here
at org.springframework.orm.hibernate3.SpringSessionContext.currentSession(SpringSessionContext.java:63)
at org.hibernate.impl.SessionFactoryImpl.getCurrentSession(SessionFactoryImpl.java:687)
at org.codehaus.groovy.grails.orm.hibernate.SessionFactoryProxy.getCurrentSession(SessionFactoryProxy.java:145)
at org.codehaus.groovy.grails.orm.hibernate.validation.HibernateDomainClassValidator.validate(HibernateDomainClassValidator.java:51)
at org.codehaus.groovy.grails.validation.GrailsDomainClassValidator.validate(GrailsDomainClassValidator.java:121)
at org.codehaus.groovy.grails.orm.hibernate.metaclass.ValidatePersistentMethod.doInvokeInternal(ValidatePersistentMethod.java:119)
at org.codehaus.groovy.grails.orm.hibernate.metaclass.AbstractDynamicPersistentMethod.invoke(AbstractDynamicPersistentMethod.java:63)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at com.springsource.loaded.ri.ReflectiveInterceptor.jlrMethodInvoke(ReflectiveInterceptor.java:1231)
at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite$PojoCachedMethodSite.invoke(PojoMetaMethodSite.java:189)
at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite.call(PojoMetaMethodSite.java:53)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:42)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:108)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:124)
at org.codehaus.groovy.grails.orm.hibernate.HibernateGormValidationApi.validate(HibernateGormEnhancer.groovy:702)
at com......MyClass.validate(MyClass.groovy)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at com.springsource.loaded.ri.ReflectiveInterceptor.jlrMethodInvoke(ReflectiveInterceptor.java:1231)
at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:90)
at org.codehaus.groovy.grails.orm.hibernate.support.ClosureEventListener$7.call(ClosureEventListener.java:282)
at org.codehaus.groovy.grails.orm.hibernate.support.ClosureEventListener$7.call(ClosureEventListener.java:267)
at org.codehaus.groovy.grails.orm.hibernate.support.ClosureEventListener.doWithManualSession(ClosureEventListener.java:302)
at org.codehaus.groovy.grails.orm.hibernate.support.ClosureEventListener.onPreUpdate(ClosureEventListener.java:267)
at org.codehaus.groovy.grails.orm.hibernate.EventTriggeringInterceptor.onPreUpdate(EventTriggeringInterceptor.java:164)
at org.codehaus.groovy.grails.orm.hibernate.EventTriggeringInterceptor.onPersistenceEvent(EventTriggeringInterceptor.java:89)
at org.grails.datastore.mapping.engine.event.AbstractPersistenceEventListener.onApplicationEvent(AbstractPersistenceEventListener.java:46)
at org.springframework.context.event.SimpleApplicationEventMulticaster$1.run(SimpleApplicationEventMulticaster.java:92)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
at java.lang.Thread.run(Thread.java:680)
I am hoping someone can enlighten me regarding:
where this worker thread pool originates from?
why is is doing a preUpdate event (when I believe the save in my thread already did a successful validate and save
Can I somehow config my app such that these foreign worker threads are not there
If #3 can't be pulled off, how can I do the equivalent of a "withTransaction" over in the worker thread code that I do not "own"