GH-8800: Fix SMB Gateway for recursive MGET

Fixes: gh-8800

The `SmbFile.getName()` returns plain file name, not with a
sub-directory as it is expected by the `AbstractRemoteFileOutboundGateway`
for (S)FTP protocols

* Introduce `AbstractRemoteFileOutboundGateway.getFullFileName()`
with dir and file name concatenation by default (as it was before)
and override it in the `SmbOutboundGateway` to return the full path according this protocol.
* Fix `SmbOutboundGateway.enhanceNameWithSubDirectory()` to return an `SmbFile` instance
since it has all the info about remote file, include requested directory
* Fix `SmbOutboundGateway.getFilename()` to remove the trailing `/` in the dir name
since this is not what expected by the `AbstractRemoteFileOutboundGateway` logic

**Cherry-pick to `6.1.x`**
This commit is contained in:
Artem Bilan
2023-12-06 10:17:11 -05:00
committed by Christian Tzolov
parent 45a32cf13c
commit 0531832a0e
3 changed files with 37 additions and 28 deletions

View File

@@ -1036,6 +1036,10 @@ public abstract class AbstractRemoteFileOutboundGateway<F> extends AbstractReply
return;
}
String fileName = getFilename(fileToAdd);
// Some remote file protocols don't include sub-dir into file name.
if (StringUtils.hasText(subDirectory) && !fileName.startsWith(subDirectory)) {
fileName = subDirectory + fileName;
}
final boolean isDirectory = isDirectory(file);
boolean isDots = hasDots(fileName);
if ((this.options.contains(Option.SUBDIRS) || !isDirectory)
@@ -1276,10 +1280,7 @@ public abstract class AbstractRemoteFileOutboundGateway<F> extends AbstractReply
private File getRemoteFileForMget(Message<?> message, Session<F> session, String remoteDirectory,
AbstractFileInfo<F> lsEntry) throws IOException {
String fullFileName =
remoteDirectory != null
? remoteDirectory + getFilename(lsEntry)
: getFilename(lsEntry);
String fullFileName = getFullFileName(remoteDirectory, lsEntry.getFileInfo());
/*
* With recursion, the filename might contain subdirectory information
* normalize each file separately.
@@ -1289,6 +1290,20 @@ public abstract class AbstractRemoteFileOutboundGateway<F> extends AbstractReply
return get(message, session, actualRemoteDirectory, fullFileName, fileName, lsEntry.getFileInfo());
}
/**
* By default, this method contacts the remote directory with the remote file name
* to build a full remote file path.
* The remote file protocol-specific implementation may override this method for other approach.
* @param remoteDirectory the directory remote file belongs.
* @param remoteFile the remote file to take a name and adjust its path according provided remote directory.
* @return the full path for the remote file
*/
protected String getFullFileName(String remoteDirectory, F remoteFile) {
return remoteDirectory != null
? remoteDirectory + getFilename(remoteFile)
: getFilename(remoteFile);
}
private String getRemoteDirectory(String remoteFilePath, String remoteFilename) {
String remoteDir = remoteFilePath.substring(0, remoteFilePath.lastIndexOf(remoteFilename));
if (remoteDir.length() == 0) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2022 the original author or authors.
* Copyright 2022-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,8 +16,6 @@
package org.springframework.integration.smb.outbound;
import java.net.MalformedURLException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@@ -145,13 +143,14 @@ public class SmbOutboundGateway extends AbstractRemoteFileOutboundGateway<SmbFil
}
@Override
protected String getFilename(SmbFile file) {
return file.getName();
protected String getFilename(AbstractFileInfo<SmbFile> file) {
return getFilename(file.getFileInfo());
}
@Override
protected String getFilename(AbstractFileInfo<SmbFile> file) {
return file.getFilename();
protected String getFilename(SmbFile file) {
String name = file.getName();
return name.endsWith("/") ? name.substring(0, name.length() - 1) : name;
}
@Override
@@ -170,14 +169,12 @@ public class SmbOutboundGateway extends AbstractRemoteFileOutboundGateway<SmbFil
@Override
protected SmbFile enhanceNameWithSubDirectory(SmbFile file, String directory) {
try {
file.renameTo(new SmbFile(file, directory), true);
return file;
}
catch (SmbException | MalformedURLException | UnknownHostException e) {
logger.error("Unable to enhance file name with a sub directory path", e);
return null;
}
return file;
}
@Override
protected String getFullFileName(String remoteDirectory, SmbFile smbFile) {
return smbFile.getLocator().getURLPath().substring(1).replaceFirst(smbFile.getShare() + "/?", "");
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2022 the original author or authors.
* Copyright 2022-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -291,15 +291,14 @@ public class SmbTests extends SmbTestSupport {
Smb.outboundGateway(sessionFactory(), AbstractRemoteFileOutboundGateway.Command.MGET, "payload")
.options(AbstractRemoteFileOutboundGateway.Option.RECURSIVE)
.fileExistsMode(FileExistsMode.IGNORE)
.filterExpression("name matches 'subSmbSource|.*.txt'")
.filterExpression("name matches 'smbSource/|subSmbSource/|subSmbSource\\d\\.txt'")
.localDirectoryExpression("'" + getTargetLocalDirectoryName() + "' + #remoteDirectory")
.localFilenameExpression("#remoteFileName.replaceFirst('smbSource', 'localTarget')")
.localFilenameExpression("#remoteFileName.replaceFirst('subSmbSource', 'localTarget')")
.charset(StandardCharsets.UTF_8.name())
.useTemporaryFileName(true))
.channel(out);
IntegrationFlowRegistration registration = this.flowContext.registration(flow).register();
String dir = "smbSource/subSmbSource/";
registration.getInputChannel().send(new GenericMessage<>(dir + "*"));
registration.getInputChannel().send(new GenericMessage<>("*"));
Message<?> result = out.receive(10_000);
assertThat(result).isNotNull();
@@ -307,12 +306,10 @@ public class SmbTests extends SmbTestSupport {
assertThat(localFiles).as("unexpected local files " + localFiles).hasSize(2);
for (File file : localFiles) {
assertThat(file.getPath().replaceAll(Matcher.quoteReplacement(File.separator), "/")).contains(dir);
assertThat(file.getPath().replaceAll(Matcher.quoteReplacement(File.separator), "/"))
.matches(".*smbSource/subSmbSource/localTarget\\d.txt");
}
assertThat(localFiles.get(1).getPath().replaceAll(Matcher.quoteReplacement(File.separator), "/"))
.contains(dir + "subSmbSource");
registration.destroy();
}