Skip to content

Commit 9019676

Browse files
committed
GH-3370: Remove synchronized from RemoteFileUtils (#3380)
* GH-3370: Remove synchronized from RemoteFileUtils Fixes #3370 The `synchronized` on the `RemoteFileUtils.makeDirectories()` makes an application too slow, especially when we deal with different paths in different sessions * Remove the `synchronized` from that method and rework `SftpSession.mkdir()` to return `false` when "A file cannot be created if it already exists" exception is thrown from the server. Essentially make an `exists()` call to be sure that an exception is really related to "file-already-exists" answer from the server **Cherry-pick to 5.3.x, 5.2.x & 4.3.x** * * Re-throw an exception in the `SftpSession.mkdir()` when error code is not `4` or remote dir does not exist * * Check `session.mkdir()` result in the `RemoteFileUtils` to throw an `IOException` when `false` * * Fix mock test to return `true` for `mkdir` instead of `null` # Conflicts: # spring-integration-file/src/main/java/org/springframework/integration/file/remote/RemoteFileUtils.java # spring-integration-file/src/main/java/org/springframework/integration/file/remote/gateway/AbstractRemoteFileOutboundGateway.java # spring-integration-file/src/test/java/org/springframework/integration/file/remote/gateway/RemoteFileOutboundGatewayTests.java # spring-integration-sftp/src/test/java/org/springframework/integration/sftp/outbound/SftpOutboundTests.java
1 parent 059d474 commit 9019676

File tree

5 files changed

+52
-45
lines changed

5 files changed

+52
-45
lines changed

spring-integration-file/src/main/java/org/springframework/integration/file/remote/RemoteFileUtils.java

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2016 the original author or authors.
2+
* Copyright 2002-2020 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -27,6 +27,8 @@
2727
/**
2828
* Utility methods for supporting remote file operations.
2929
* @author Gary Russell
30+
* @author Artem Bilan
31+
*
3032
* @since 3.0
3133
*
3234
*/
@@ -45,13 +47,11 @@ private RemoteFileUtils() {
4547
* @param logger The logger.
4648
* @throws IOException Any IOException.
4749
*/
48-
public static synchronized <F> void makeDirectories(String path, Session<F> session, String remoteFileSeparator,
50+
public static <F> void makeDirectories(String path, Session<F> session, String remoteFileSeparator,
4951
Log logger) throws IOException {
5052

5153
if (!session.exists(path)) {
52-
5354
int nextSeparatorIndex = path.lastIndexOf(remoteFileSeparator);
54-
5555
if (nextSeparatorIndex > -1) {
5656
List<String> pathsToCreate = new LinkedList<String>();
5757
while (nextSeparatorIndex > -1) {
@@ -70,13 +70,19 @@ public static synchronized <F> void makeDirectories(String path, Session<F> sess
7070
if (logger.isDebugEnabled()) {
7171
logger.debug("Creating '" + pathToCreate + "'");
7272
}
73-
session.mkdir(pathToCreate);
73+
tryCreateRemoteDirectory(session, pathToCreate);
7474
}
7575
}
7676
else {
77-
session.mkdir(path);
77+
tryCreateRemoteDirectory(session, path);
7878
}
7979
}
8080
}
8181

82+
private static void tryCreateRemoteDirectory(Session<?> session, String path) throws IOException {
83+
if (!session.mkdir(path)) {
84+
throw new IOException("Could not create a remote directory: " + path);
85+
}
86+
}
87+
8288
}

spring-integration-file/src/main/java/org/springframework/integration/file/remote/gateway/AbstractRemoteFileOutboundGateway.java

Lines changed: 34 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2017 the original author or authors.
2+
* Copyright 2002-2020 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -510,14 +510,13 @@ protected void doInit() {
510510
if (Command.MGET.equals(this.command)) {
511511
Assert.isTrue(!(this.options.contains(Option.SUBDIRS)),
512512
"Cannot use " + Option.SUBDIRS.toString() + " when using 'mget' use "
513-
+ Option.RECURSIVE.toString() + " to obtain files in subdirectories");
513+
+ Option.RECURSIVE.toString() + " to obtain files in subdirectories");
514514
}
515515

516516
if (getBeanFactory() != null) {
517517
if (this.fileNameProcessor != null) {
518518
this.fileNameProcessor.setBeanFactory(getBeanFactory());
519519
}
520-
521520
this.renameProcessor.setBeanFactory(getBeanFactory());
522521
this.remoteFileTemplate.setBeanFactory(getBeanFactory());
523522
}
@@ -527,20 +526,20 @@ protected void doInit() {
527526
protected Object handleRequestMessage(final Message<?> requestMessage) {
528527
if (this.command != null) {
529528
switch (this.command) {
530-
case LS:
531-
return doLs(requestMessage);
532-
case GET:
533-
return doGet(requestMessage);
534-
case MGET:
535-
return doMget(requestMessage);
536-
case RM:
537-
return doRm(requestMessage);
538-
case MV:
539-
return doMv(requestMessage);
540-
case PUT:
541-
return doPut(requestMessage);
542-
case MPUT:
543-
return doMput(requestMessage);
529+
case LS:
530+
return doLs(requestMessage);
531+
case GET:
532+
return doGet(requestMessage);
533+
case MGET:
534+
return doMget(requestMessage);
535+
case RM:
536+
return doRm(requestMessage);
537+
case MV:
538+
return doMv(requestMessage);
539+
case PUT:
540+
return doPut(requestMessage);
541+
case MPUT:
542+
return doMput(requestMessage);
544543
}
545544
}
546545
return this.remoteFileTemplate.execute(new SessionCallback<F, Object>() {
@@ -570,8 +569,8 @@ public List<?> doInSession(Session<F> session) throws IOException {
570569
}
571570
});
572571
return this.getMessageBuilderFactory().withPayload(payload)
573-
.setHeader(FileHeaders.REMOTE_DIRECTORY, dir)
574-
.build();
572+
.setHeader(FileHeaders.REMOTE_DIRECTORY, dir)
573+
.build();
575574
}
576575

577576
private Object doGet(final Message<?> requestMessage) {
@@ -621,9 +620,9 @@ public List<File> doInSession(Session<F> session) throws IOException {
621620
}
622621
});
623622
return this.getMessageBuilderFactory().withPayload(payload)
624-
.setHeader(FileHeaders.REMOTE_DIRECTORY, remoteDir)
625-
.setHeader(FileHeaders.REMOTE_FILE, remoteFilename)
626-
.build();
623+
.setHeader(FileHeaders.REMOTE_DIRECTORY, remoteDir)
624+
.setHeader(FileHeaders.REMOTE_FILE, remoteFilename)
625+
.build();
627626
}
628627

629628
private Object doRm(Message<?> requestMessage) {
@@ -634,24 +633,24 @@ private Object doRm(Message<?> requestMessage) {
634633
boolean payload = this.remoteFileTemplate.remove(remoteFilePath);
635634

636635
return this.getMessageBuilderFactory().withPayload(payload)
637-
.setHeader(FileHeaders.REMOTE_DIRECTORY, remoteDir)
638-
.setHeader(FileHeaders.REMOTE_FILE, remoteFilename)
639-
.build();
636+
.setHeader(FileHeaders.REMOTE_DIRECTORY, remoteDir)
637+
.setHeader(FileHeaders.REMOTE_FILE, remoteFilename)
638+
.build();
640639
}
641640

642641
private Object doMv(Message<?> requestMessage) {
643-
String remoteFilePath = this.fileNameProcessor.processMessage(requestMessage);
642+
String remoteFilePath = this.fileNameProcessor.processMessage(requestMessage);
644643
String remoteFilename = getRemoteFilename(remoteFilePath);
645644
String remoteDir = getRemoteDirectory(remoteFilePath, remoteFilename);
646645
String remoteFileNewPath = this.renameProcessor.processMessage(requestMessage);
647646
Assert.hasLength(remoteFileNewPath, "New filename cannot be empty");
648647

649648
this.remoteFileTemplate.rename(remoteFilePath, remoteFileNewPath);
650649
return this.getMessageBuilderFactory().withPayload(Boolean.TRUE)
651-
.setHeader(FileHeaders.REMOTE_DIRECTORY, remoteDir)
652-
.setHeader(FileHeaders.REMOTE_FILE, remoteFilename)
653-
.setHeader(FileHeaders.RENAME_TO, remoteFileNewPath)
654-
.build();
650+
.setHeader(FileHeaders.REMOTE_DIRECTORY, remoteDir)
651+
.setHeader(FileHeaders.REMOTE_FILE, remoteFilename)
652+
.setHeader(FileHeaders.RENAME_TO, remoteFileNewPath)
653+
.build();
655654
}
656655

657656
private String doPut(Message<?> requestMessage) {
@@ -722,7 +721,7 @@ private List<String> putLocalDirectory(Message<?> requestMessage, File file, Str
722721
else if (this.options.contains(Option.RECURSIVE)) {
723722
String newSubDirectory = (StringUtils.hasText(subDirectory) ?
724723
subDirectory + this.remoteFileTemplate.getRemoteFileSeparator() : "")
725-
+ filteredFile.getName();
724+
+ filteredFile.getName();
726725
replies.addAll(this.putLocalDirectory(requestMessage, filteredFile, newSubDirectory));
727726
}
728727
}
@@ -795,7 +794,7 @@ private List<F> listFilesInRemoteDir(Session<F> session, String directory, Strin
795794
}
796795
}
797796
if (recursion && this.isDirectory(file) && !(".".equals(fileName)) && !("..".equals(fileName))) {
798-
lsFiles.addAll(listFilesInRemoteDir(session, directory, subDirectory + fileName
797+
lsFiles.addAll(listFilesInRemoteDir(session, directory, subDirectory + fileName
799798
+ this.remoteFileTemplate.getRemoteFileSeparator()));
800799
}
801800
}
@@ -858,7 +857,7 @@ protected void purgeDots(List<F> lsFiles) {
858857
* @throws IOException Any IOException.
859858
*/
860859
protected File get(Message<?> message, Session<F> session, String remoteDir, String remoteFilePath,
861-
String remoteFilename, boolean lsFirst) throws IOException {
860+
String remoteFilename, boolean lsFirst) throws IOException {
862861
F[] files = null;
863862
if (lsFirst) {
864863
files = session.list(remoteFilePath);
@@ -931,7 +930,7 @@ else if (FileExistsMode.IGNORE != fileExistsMode) {
931930
}
932931

933932
protected List<File> mGet(Message<?> message, Session<F> session, String remoteDirectory,
934-
String remoteFilename) throws IOException {
933+
String remoteFilename) throws IOException {
935934
if (this.options.contains(Option.RECURSIVE)) {
936935
if (logger.isWarnEnabled() && !("*".equals(remoteFilename))) {
937936
logger.warn("File name pattern must be '*' when using recursion");
@@ -1018,7 +1017,7 @@ private List<File> mGetWithRecursion(Message<?> message, Session<F> session, Str
10181017
String fileName = this.getRemoteFilename(fullFileName);
10191018
String actualRemoteDirectory = this.getRemoteDirectory(fullFileName, fileName);
10201019
File file = get(message, session, actualRemoteDirectory,
1021-
fullFileName, fileName, false);
1020+
fullFileName, fileName, false);
10221021
if (this.options.contains(Option.PRESERVE_TIMESTAMP)) {
10231022
file.setLastModified(getModified(lsEntry.getFileInfo()));
10241023
}

spring-integration-file/src/test/java/org/springframework/integration/file/remote/gateway/RemoteFileOutboundGatewayTests.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -323,7 +323,7 @@ public Object answer(InvocationOnMock invocation) throws Throwable {
323323
@Override
324324
public Object answer(InvocationOnMock invocation) throws Throwable {
325325
madeDirs.add((String) invocation.getArguments()[0]);
326-
return null;
326+
return true;
327327
}
328328
}).when(session).mkdir(anyString());
329329
when(sessionFactory.getSession()).thenReturn(session);

spring-integration-sftp/src/main/java/org/springframework/integration/sftp/session/SftpSession.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -229,8 +229,10 @@ public boolean mkdir(String remoteDirectory) throws IOException {
229229
try {
230230
this.channel.mkdir(remoteDirectory);
231231
}
232-
catch (SftpException e) {
233-
throw new NestedIOException("failed to create remote directory '" + remoteDirectory + "'.", e);
232+
catch (SftpException ex) {
233+
if (ex.id != ChannelSftp.SSH_FX_FAILURE || !exists(remoteDirectory)) {
234+
throw new NestedIOException("failed to create remote directory '" + remoteDirectory + "'.", ex);
235+
}
234236
}
235237
return true;
236238
}

spring-integration-sftp/src/test/java/org/springframework/integration/sftp/outbound/SftpOutboundTests.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ public void testMkDir() throws Exception {
216216
@Override
217217
public Object answer(InvocationOnMock invocation) throws Throwable {
218218
madeDirs.add((String) invocation.getArguments()[0]);
219-
return null;
219+
return true;
220220
}
221221
}).when(session).mkdir(anyString());
222222
handler.handleMessage(new GenericMessage<String>("qux"));

0 commit comments

Comments
 (0)