Skip to content

The shared SFTP session is closed prematurely #9909

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
artembilan opened this issue Mar 18, 2025 · 5 comments
Closed

The shared SFTP session is closed prematurely #9909

artembilan opened this issue Mar 18, 2025 · 5 comments

Comments

@artembilan
Copy link
Member

With the new DefaultSftpSessionFactory(true), the SftpSession.close() leads to the problem in concurrent environment:

java.io.UncheckedIOException: org.apache.sshd.common.SshException: Channel is being closed

More info in: apache/mina-sshd#527

Right now the workaround is to use CachingSessionFactory which does not initiate a physical close of the client session.

This is a side effect of the fix from: #9272.

@artembilan artembilan added this to the 6.5.0-M3 milestone Mar 18, 2025
@artembilan artembilan self-assigned this Mar 18, 2025
spring-builds pushed a commit that referenced this issue Mar 18, 2025
Fixes: #9909
Issue link: #9909

The `SftpSession.close()` closes `sftpClient` and its `clientSession` unconditionally.
At the same time the `DefaultSftpSessionFactory.isSharedSession` is expected to expose only a single shared client.
When this `DefaultSftpSessionFactory` is used concurrently, there is a chance that one thread would close
that shared client and another won't be able to interact due to `clientSession` is closed.

* Fix `SftpSession` accepting an `isSharedClient` flag and doing nothing in the `close()` if client is shared.
* Propagate `isSharedSession` state down to the `SftpSession` from the `DefaultSftpSessionFactory`
* Closed shared client and its `clientSession` in the `DefaultSftpSessionFactory.destroy()`

(cherry picked from commit 2df9611)
artembilan added a commit that referenced this issue Mar 18, 2025
Fixes: #9909
Issue link: #9909

The `SftpSession.close()` closes `sftpClient` and its `clientSession` unconditionally.
At the same time the `DefaultSftpSessionFactory.isSharedSession` is expected to expose only a single shared client.
When this `DefaultSftpSessionFactory` is used concurrently, there is a chance that one thread would close
that shared client and another won't be able to interact due to `clientSession` is closed.

* Fix `SftpSession` accepting an `isSharedClient` flag and doing nothing in the `close()` if client is shared.
* Propagate `isSharedSession` state down to the `SftpSession` from the `DefaultSftpSessionFactory`
* Closed shared client and its `clientSession` in the `DefaultSftpSessionFactory.destroy()`

(cherry picked from commit 2df9611)

# Conflicts:
#	spring-integration-sftp/src/test/java/org/springframework/integration/sftp/session/SftpSessionFactoryTests.java
@cherrymoonba
Copy link

@artembilan We are facing issues with this in single threaded environment too, with combination of shared SFTP session set to true and using CachingSessionFactory. Attaching the logs for your review.

org.springframework.messaging.MessageDeliveryException: Error handling message for file [D:\_source\20250317124745.txt -> 20250317124745.txt]

	at org.springframework.integration.file.remote.RemoteFileTemplate.doSend(RemoteFileTemplate.java:367)

	at org.springframework.integration.file.remote.RemoteFileTemplate.lambda$send$0(RemoteFileTemplate.java:314)

	at org.springframework.integration.file.remote.RemoteFileTemplate.execute(RemoteFileTemplate.java:452)

	at org.springframework.integration.file.remote.RemoteFileTemplate.send(RemoteFileTemplate.java:314)

	at org.springframework.integration.file.remote.RemoteFileTemplate.send(RemoteFileTemplate.java:302)

	at org.springframework.integration.file.remote.RemoteFileTemplate.send(RemoteFileTemplate.java:294)

	at org.springframework.integration.file.remote.handler.FileTransferringMessageHandler.handleMessageInternal(FileTransferringMessageHandler.java:207)

	at org.springframework.integration.handler.AbstractMessageHandler.doHandleMessage(AbstractMessageHandler.java:105)

	at org.springframework.integration.handler.AbstractMessageHandler.handleMessage(AbstractMessageHandler.java:73)

	at org.springframework.integration.handler.ReplyProducingMessageHandlerWrapper.handleRequestMessage(ReplyProducingMessageHandlerWrapper.java:59)

	at org.springframework.integration.handler.AbstractReplyProducingMessageHandler.handleMessageInternal(AbstractReplyProducingMessageHandler.java:136)

	at org.springframework.integration.handler.AbstractMessageHandler.doHandleMessage(AbstractMessageHandler.java:105)

	at org.springframework.integration.handler.AbstractMessageHandler.handleMessage(AbstractMessageHandler.java:73)

	at org.springframework.integration.dispatcher.AbstractDispatcher.tryOptimizedDispatch(AbstractDispatcher.java:115)

	at org.springframework.integration.dispatcher.UnicastingDispatcher.doDispatch(UnicastingDispatcher.java:133)

	at org.springframework.integration.dispatcher.UnicastingDispatcher.dispatch(UnicastingDispatcher.java:106)

	at org.springframework.integration.channel.AbstractSubscribableChannel.doSend(AbstractSubscribableChannel.java:72)

	at org.springframework.integration.channel.AbstractMessageChannel.sendInternal(AbstractMessageChannel.java:375)

	at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:329)

	at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:187)

	at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:166)

	at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:47)

	at org.springframework.messaging.core.AbstractMessageSendingTemplate.send(AbstractMessageSendingTemplate.java:109)

	at org.springframework.integration.gateway.MessagingGatewaySupport.doSend(MessagingGatewaySupport.java:517)

	at org.springframework.integration.gateway.MessagingGatewaySupport.send(MessagingGatewaySupport.java:485)

	at org.springframework.integration.gateway.GatewayProxyFactoryBean.sendOrSendAndReceive(GatewayProxyFactoryBean.java:669)

	at org.springframework.integration.gateway.GatewayProxyFactoryBean.invokeGatewayMethod(GatewayProxyFactoryBean.java:584)

	at org.springframework.integration.gateway.GatewayProxyFactoryBean.doInvoke(GatewayProxyFactoryBean.java:550)

	at org.springframework.integration.gateway.GatewayProxyFactoryBean.invoke(GatewayProxyFactoryBean.java:540)

	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)

	at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:244)

	at jdk.proxy2/jdk.proxy2.$Proxy154.upload(Unknown Source)

	at com.wk.gbs.txnbilling.batch.data.src.job.AbstractBatchSrcConfigStep10_SftpSAP$1.doSftp(AbstractBatchSrcConfigStep10_SftpSAP.java:85)

	at com.wk.gbs.txnbilling.batch.data.src.job.AbstractBatchSrcConfigStep10_SftpSAP$1.execute(AbstractBatchSrcConfigStep10_SftpSAP.java:61)

	at org.springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:389)

	at org.springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:313)

	at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:140)

	at org.springframework.batch.core.step.tasklet.TaskletStep$2.doInChunkContext(TaskletStep.java:256)

	at org.springframework.batch.core.scope.context.StepContextRepeatCallback.doInIteration(StepContextRepeatCallback.java:82)

	at org.springframework.batch.repeat.support.RepeatTemplate.getNextResult(RepeatTemplate.java:369)

	at org.springframework.batch.repeat.support.RepeatTemplate.executeInternal(RepeatTemplate.java:206)

	at org.springframework.batch.repeat.support.RepeatTemplate.iterate(RepeatTemplate.java:140)

	at org.springframework.batch.core.step.tasklet.TaskletStep.doExecute(TaskletStep.java:241)

	at org.springframework.batch.core.step.AbstractStep.execute(AbstractStep.java:229)

	at org.springframework.batch.core.job.SimpleStepHandler.handleStep(SimpleStepHandler.java:153)

	at org.springframework.batch.core.job.flow.JobFlowExecutor.executeStep(JobFlowExecutor.java:68)

	at org.springframework.batch.core.job.flow.support.state.StepState.handle(StepState.java:68)

	at org.springframework.batch.core.job.flow.support.SimpleFlow.resume(SimpleFlow.java:165)

	at org.springframework.batch.core.job.flow.support.SimpleFlow.start(SimpleFlow.java:140)

	at org.springframework.batch.core.job.flow.FlowJob.doExecute(FlowJob.java:132)

	at org.springframework.batch.core.job.AbstractJob.execute(AbstractJob.java:317)

	at org.springframework.batch.core.launch.support.SimpleJobLauncher$1.run(SimpleJobLauncher.java:157)

	at org.springframework.core.task.SyncTaskExecutor.execute(SyncTaskExecutor.java:50)

	at org.springframework.batch.core.launch.support.SimpleJobLauncher.run(SimpleJobLauncher.java:148)

	at org.springframework.batch.core.launch.support.TaskExecutorJobLauncher.run(TaskExecutorJobLauncher.java:59)

	at com.wk.gbs.txnbilling.batch.data.flow.job.FileStagedEventListener.launchJob(FileStagedEventListener.java:170)

	at com.wk.gbs.txnbilling.batch.data.flow.job.FileStagedEventListener.process(FileStagedEventListener.java:106)

	at com.wk.gbs.txnbilling.batch.data.flow.job.FileStagedEventListener.runSync(FileStagedEventListener.java:70)

	at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)

	at java.base/java.lang.reflect.Method.invoke(Method.java:580)

	at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:343)

	at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:699)

	at com.wk.gbs.txnbilling.batch.data.flow.job.FileStagedEventListener$$SpringCGLIB$$0.runSync(<generated>)

	at com.wk.gbs.txnbilling.batch.data.flow.job.FileStagedEventListenerScheduler.trigger(FileStagedEventListenerScheduler.java:36)

	at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)

	at java.base/java.lang.reflect.Method.invoke(Method.java:580)

	at org.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:84)

	at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54)

	at org.springframework.scheduling.concurrent.ReschedulingRunnable.run(ReschedulingRunnable.java:96)

	at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:572)

	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317)

	at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304)

	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144)

	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642)

	at java.base/java.lang.Thread.run(Thread.java:1583)

Caused by: org.springframework.messaging.MessagingException: Failed to write to 'RD7/BackOffice/AMS/TransBill/Inbound/CLNTCOL_106507_20250317124745.txt.writing' while uploading the file

	at org.springframework.integration.file.remote.RemoteFileTemplate.sendFileToRemoteDirectory(RemoteFileTemplate.java:586)

	at org.springframework.integration.file.remote.RemoteFileTemplate.doSend(RemoteFileTemplate.java:353)

	... 74 common frames omitted

Caused by: org.apache.sshd.common.SshException: Channel is being closed

	at org.apache.sshd.sftp.client.impl.DefaultSftpClient.receive(DefaultSftpClient.java:317)

	at org.apache.sshd.sftp.client.impl.AbstractSftpClient.checkHandle(AbstractSftpClient.java:231)

	at org.apache.sshd.sftp.client.impl.AbstractSftpClient.open(AbstractSftpClient.java:573)

	at org.apache.sshd.sftp.client.impl.SftpOutputStreamAsync.<init>(SftpOutputStreamAsync.java:63)

	at org.apache.sshd.sftp.client.impl.AbstractSftpClient.write(AbstractSftpClient.java:1201)

	at org.apache.sshd.sftp.client.SftpClient.write(SftpClient.java:940)

	at org.apache.sshd.sftp.client.SftpClient.write(SftpClient.java:936)

	at org.springframework.integration.sftp.session.SftpSession.write(SftpSession.java:125)

	at org.springframework.integration.file.remote.session.CachingSessionFactory$CachedSession.write(CachingSessionFactory.java:237)

	at org.springframework.integration.file.remote.RemoteFileTemplate.doSend(RemoteFileTemplate.java:595)

	at org.springframework.integration.file.remote.RemoteFileTemplate.sendFileToRemoteDirectory(RemoteFileTemplate.java:583)

	... 75 common frames omitted

@artembilan
Copy link
Member Author

@cherrymoonba ,

The issue has been fixed and releases yesterday. Please consider to upgrade . You must not use shared and caching together. Shared is already cached. However I agree that with the problem we had so far it still may fail. Because, well , it is shared.
Now, give it a shot after the fix.

I’m not why you say “single threaded” since Spring Batch is not like that.

@cherrymoonba
Copy link

@artembilan , sorry, I meant our batch job runs steps sequentially in a single thread where we use CachingSessionFactory.

@artembilan
Copy link
Member Author

Doesn’t look like . Your stack trace contains a ThreadPoolExecutor.

@cherrymoonba
Copy link

@artembilan Sorry my bad, let me look into this more. Thanks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants