GH-8745: Add RFT.shouldMarkSessionAsDirty() (#8759)

* GH-8745: Add RFT.shouldMarkSessionAsDirty()

Fixes https://github.com/spring-projects/spring-integration/issues/8745

Not all errors caught in the `RemoteFileTemplate.execute()`
are fatal to mark session as dirty and physically close the target session
in the cache

* Introduce a `RemoteFileTemplate.shouldMarkSessionAsDirty()`
to consult with an exception if it is really a fatal error to close
the session in the end.
* Override `shouldMarkSessionAsDirty()` in the `RemoteFileTemplate`
implementations to check statuses of respective protocol errors

**Cherry-pick to `6.1.x` & `6.0.x`**

* * Fix tests for pool interaction

* * Fix language in Javadocs
* Add more `not dirty` statuses to `SftpRemoteFileTemplate` & `SmbRemoteFileTemplate`
This commit is contained in:
Artem Bilan
2023-10-11 10:52:48 -04:00
committed by GitHub
parent b8228531d5
commit 44433ed8a1
7 changed files with 239 additions and 44 deletions

View File

@@ -451,27 +451,42 @@ public class RemoteFileTemplate<F> implements RemoteFileOperations<F>, Initializ
}
return callback.doInSession(session);
}
catch (Exception e) {
if (session != null) {
catch (Exception ex) {
if (session != null && shouldMarkSessionAsDirty(ex)) {
session.dirty();
}
if (e instanceof MessagingException) { // NOSONAR
throw (MessagingException) e;
if (ex instanceof MessagingException messagingException) { // NOSONAR
throw messagingException;
}
throw new MessagingException("Failed to execute on session", e);
throw new MessagingException("Failed to execute on session", ex);
}
finally {
if (!invokeScope && session != null) {
try {
session.close();
}
catch (Exception ignored) {
this.logger.debug("failed to close Session", ignored);
catch (Exception ex) {
this.logger.debug("failed to close Session", ex);
}
}
}
}
/**
* Determine whether {@link Session#dirty()} should be called
* in the {@link #execute(SessionCallback)} when an exception is thrown from the callback.
* By default, this method returns {@code true}.
* Remote file protocol extensions can override this method to provide
* a specific strategy against the thrown exception, e.g. {@code file not found} error
* is not a signal that session is broken.
* @param ex the exception to check if {@link Session} must be marked as dirty.
* @return true if {@link Session#dirty()} should be called.
* @since 6.0.8
*/
protected boolean shouldMarkSessionAsDirty(Exception ex) {
return true;
}
@Override
public <T> T invoke(OperationsCallback<F, T> action) {
Session<F> contextSession = this.contextSessions.get();
@@ -503,8 +518,7 @@ public class RemoteFileTemplate<F> implements RemoteFileOperations<F>, Initializ
private StreamHolder payloadToInputStream(Message<?> message) throws MessageDeliveryException {
Object payload = message.getPayload();
try {
if (payload instanceof File) {
File inputFile = (File) payload;
if (payload instanceof File inputFile) {
if (inputFile.exists()) {
return new StreamHolder(
new BufferedInputStream(new FileInputStream(inputFile)), inputFile.getAbsolutePath());
@@ -526,8 +540,7 @@ public class RemoteFileTemplate<F> implements RemoteFileOperations<F>, Initializ
else if (payload instanceof InputStream) {
return new StreamHolder((InputStream) payload, "InputStream payload");
}
else if (payload instanceof Resource) {
Resource resource = (Resource) payload;
else if (payload instanceof Resource resource) {
String filename = resource.getFilename();
return new StreamHolder(resource.getInputStream(), filename != null ? filename : "Resource payload");
}
@@ -619,16 +632,7 @@ public class RemoteFileTemplate<F> implements RemoteFileOperations<F>, Initializ
}
}
private static final class StreamHolder {
private final InputStream stream;
private final String name;
StreamHolder(InputStream stream, String name) {
this.stream = stream;
this.name = name;
}
private record StreamHolder(InputStream stream, String name) {
}