[SSHD-893] Using Path(s) instead of String(s) as DirectoryScanner results
authorLyor Goldstein <lgoldstein@apache.org>
Mon, 11 Feb 2019 13:41:52 +0000 (15:41 +0200)
committerLyor Goldstein <lgoldstein@apache.org>
Tue, 12 Feb 2019 10:02:19 +0000 (12:02 +0200)
CHANGES.md
docs/scp.md
sshd-common/src/main/java/org/apache/sshd/common/util/io/DirectoryScanner.java
sshd-common/src/test/java/org/apache/sshd/common/util/io/DirectoryScannerTest.java
sshd-scp/src/main/java/org/apache/sshd/common/scp/ScpFileOpener.java
sshd-scp/src/main/java/org/apache/sshd/common/scp/ScpHelper.java
sshd-scp/src/test/java/org/apache/sshd/client/scp/ScpTest.java

index 3ae98eb..0c43e89 100644 (file)
@@ -10,6 +10,9 @@ in a similar manner as the "regular" data. Please read the relevant section in t
 * The user can use a registered `SessionDisconnectHandler` in order be informed and also intervene in cases
 where the code decides to disconnect the session due to various protocol or configuration parameters violations.
 
+* `ScpFileOpener#getMatchingFilesToSend` has been modified to accept a `Path` as the base directory
+and also return an `Iterable<Path>`.
+
 ## Behavioral changes and enhancements
 
 * [SSHD-882](https://issues.apache.org/jira/browse/SSHD-882) - Provide hooks to allow users to register a consumer
@@ -17,3 +20,5 @@ for STDERR data sent via the `ChannelSession` - especially for the SFTP subsyste
 
 * [SSHD=892](https://issues.apache.org/jira/browse/SSHD-882) - Inform user about possible session disconnect prior
 to disconnecting and allow intervention via `SessionDisconnectHandler`.
+
+* [SSHD-893] Using Path(s) instead of String(s) as DirectoryScanner results
index 05ef79e..7176e45 100644 (file)
@@ -74,6 +74,14 @@ ScpClient client2 = creator.createScpClient(session, new SomeOtherOpener());   /
 **Note:** due to SCP protocol limitations one cannot change the **size** of the input/output since it is passed as part of the command
 **before** the file opener is invoked - so there are a few limitations on what one can do within this interface implementation.
 
+In this context, note that patterns used in `ScpFileOpener#getMatchingFilesToSend` are matched using case sensitivity derived from the O/S:
+
+* `Windows` - case insensitive
+* `Unix=true` - case sensitive
+
+as detected by the internal `OsUtils`. If a different behavior is required, then one needs to replace the default opener with one
+that uses a different sensitivity via `DirectoryScanner#setCaseSensitive` call (or executes the pattern matching in another way).
+
 #### ScpTransferEventListener(s)
 
 The `ScpClientCreator` can also be used to attach a default `ScpTransferEventListener` that will be attached to
index c75c889..9758fac 100644 (file)
@@ -227,13 +227,13 @@ public class DirectoryScanner {
      * @return the matching files
      * @throws IllegalStateException if the base directory was set incorrectly
      * (i.e. if it is {@code null}, doesn't exist, or isn't a directory).
-     * @throws IOExcepion if failed to scan the directory (e.g., access denied)
+     * @throws IOException if failed to scan the directory (e.g., access denied)
      */
-    public Collection<String> scan() throws IOException, IllegalStateException {
+    public Collection<Path> scan() throws IOException, IllegalStateException {
         return scan(LinkedList::new);
     }
 
-    public <C extends Collection<String>> C scan(Supplier<? extends C> factory) throws IOException, IllegalStateException {
+    public <C extends Collection<Path>> C scan(Supplier<? extends C> factory) throws IOException, IllegalStateException {
         Path dir = getBasedir();
         if (dir == null) {
             throw new IllegalStateException("No basedir set");
@@ -248,7 +248,7 @@ public class DirectoryScanner {
             throw new IllegalStateException("No includes set for " + dir);
         }
 
-        return scandir(dir, "", factory.get());
+        return scandir(dir, dir, factory.get());
     }
 
     /**
@@ -265,21 +265,21 @@ public class DirectoryScanner {
      * path matches
      * @throws IOException if failed to scan the directory
      */
-    protected <C extends Collection<String>> C scandir(Path dir, String vpath, C filesList) throws IOException {
+    protected <C extends Collection<Path>> C scandir(Path rootDir, Path dir, C filesList) throws IOException {
         try (DirectoryStream<Path> ds = Files.newDirectoryStream(dir)) {
             for (Path p : ds) {
-                Path n = p.getFileName();
-                String name = vpath + n;
+                Path rel = rootDir.relativize(p);
+                String name = rel.toString();
                 if (Files.isDirectory(p)) {
                     if (isIncluded(name)) {
-                        filesList.add(name);
-                        scandir(p, name + File.separator, filesList);
+                        filesList.add(p);
+                        scandir(rootDir, p, filesList);
                     } else if (couldHoldIncluded(name)) {
-                        scandir(p, name + File.separator, filesList);
+                        scandir(rootDir, p, filesList);
                     }
                 } else if (Files.isRegularFile(p)) {
                     if (isIncluded(name)) {
-                        filesList.add(name);
+                        filesList.add(p);
                     }
                 }
             }
index 86f488b..0f7f27b 100644 (file)
@@ -53,23 +53,21 @@ public class DirectoryScannerTest extends JUnitTestSupport {
         Path rootDir = getTempTargetRelativeFile(getClass().getSimpleName(), getCurrentTestName());
         CommonTestSupportUtils.deleteRecursive(rootDir);    // start fresh
 
-        List<String> expected = new ArrayList<>();
+        List<Path> expected = new ArrayList<>();
         Path curLevel = rootDir;
         for (int level = 1; level <= 3; level++) {
             Path dir = Files.createDirectories(curLevel.resolve(Integer.toString(level)));
-            Path name = rootDir.relativize(dir);
-            expected.add(name.toString());
+            expected.add(dir);
             Path file = dir.resolve(Integer.toString(level) + ".txt");
             Files.write(file, Collections.singletonList(file.toString()), StandardCharsets.UTF_8);
 
-            name = rootDir.relativize(file);
-            expected.add(name.toString());
+            expected.add(file);
             curLevel = dir;
         }
         Collections.sort(expected);
 
         DirectoryScanner ds = new DirectoryScanner(rootDir, "**/*");
-        List<String> actual = ds.scan(ArrayList::new);
+        List<Path> actual = ds.scan(ArrayList::new);
         Collections.sort(actual);
         assertListEquals(getCurrentTestName(), expected, actual);
     }
@@ -80,19 +78,19 @@ public class DirectoryScannerTest extends JUnitTestSupport {
         CommonTestSupportUtils.deleteRecursive(rootDir);    // start fresh
         Files.createDirectories(rootDir);
 
-        List<String> expected = new ArrayList<>();
+        List<Path> expected = new ArrayList<>();
         for (int level = 1; level <= Byte.SIZE; level++) {
             Path file = rootDir.resolve(Integer.toString(level) + (((level & 0x03) == 0) ? ".csv" : ".txt"));
             Files.write(file, Collections.singletonList(file.toString()), StandardCharsets.UTF_8);
             String name = Objects.toString(file.getFileName());
             if (name.endsWith(".txt")) {
-                expected.add(name);
+                expected.add(file);
             }
         }
         Collections.sort(expected);
 
         DirectoryScanner ds = new DirectoryScanner(rootDir, "*.txt");
-        List<String> actual = ds.scan(ArrayList::new);
+        List<Path> actual = ds.scan(ArrayList::new);
         Collections.sort(actual);
         assertListEquals(getCurrentTestName(), expected, actual);
     }
index 1d8f5cd..5f4c715 100644 (file)
@@ -120,7 +120,7 @@ public interface ScpFileOpener {
      * @return The matching <U>relative paths</U> of the children to send
      * @throws IOException If failed to scan the directory
      */
-    default Iterable<String> getMatchingFilesToSend(Session session, Path basedir, String pattern) throws IOException {
+    default Iterable<Path> getMatchingFilesToSend(Session session, Path basedir, String pattern) throws IOException {
         DirectoryScanner ds = new DirectoryScanner(basedir, pattern);
         return ds.scan();
     }
index fed18d5..f452ee3 100644 (file)
@@ -402,25 +402,26 @@ public class ScpHelper extends AbstractLoggingBean implements SessionHolder<Sess
 
                 Session session = getSession();
                 Path basePath = resolveLocalPath(basedir);
-                Iterable<String> included =
+                Iterable<Path> included =
                     opener.getMatchingFilesToSend(session, basePath, pattern);
-                for (String path : included) {
-                    Path file = basePath.resolve(path);
+                for (Path file : included) {
                     if (opener.sendAsRegularFile(session, file, options)) {
                         sendFile(file, preserve, bufferSize);
                     } else if (opener.sendAsDirectory(session, file, options)) {
                         if (!recursive) {
                             if (debugEnabled) {
-                                log.debug("send({}) {}: not a regular file", this, path);
+                                log.debug("send({}) {}: not a regular file", this, file);
                             }
+                            String path = basePath.relativize(file).toString();
                             sendWarning(path.replace(File.separatorChar, '/') + " not a regular file");
                         } else {
                             sendDir(file, preserve, bufferSize);
                         }
                     } else {
                         if (debugEnabled) {
-                            log.debug("send({}) {}: unknown file type", this, path);
+                            log.debug("send({}) {}: unknown file type", this, file);
                         }
+                        String path = basePath.relativize(file).toString();
                         sendWarning(path.replace(File.separatorChar, '/') + " unknown file type");
                     }
                 }
index 7f5955d..b6b27a9 100644 (file)
@@ -591,9 +591,11 @@ public class ScpTest extends BaseTestSupport {
 
     @Test
     public void testScpVirtualOnDirWithPattern() throws Exception {
-        Path remoteDir = getTempTargetRelativeFile(getClass().getSimpleName(), getCurrentTestName(), ScpHelper.SCP_COMMAND_PREFIX, "virtual");
+        Path remoteDir = getTempTargetRelativeFile(
+            getClass().getSimpleName(), getCurrentTestName(), ScpHelper.SCP_COMMAND_PREFIX, "virtual");
         CommonTestSupportUtils.deleteRecursive(remoteDir);  // start fresh
         Files.createDirectories(remoteDir);
+        sshd.setFileSystemFactory(new VirtualFileSystemFactory(remoteDir));
 
         try (ClientSession session = client.connect(getCurrentTestName(), TEST_LOCALHOST, port)
                 .verify(CONNECT_TIMEOUT, TimeUnit.SECONDS)
@@ -601,8 +603,6 @@ public class ScpTest extends BaseTestSupport {
             session.addPasswordIdentity(getCurrentTestName());
             session.auth().verify(AUTH_TIMEOUT, TimeUnit.SECONDS);
 
-            sshd.setFileSystemFactory(new VirtualFileSystemFactory(remoteDir));
-
             ScpClient scp = createScpClient(session);
             Path targetPath = detectTargetFolder();
             Path scpRoot = CommonTestSupportUtils.resolve(targetPath,
@@ -634,8 +634,6 @@ public class ScpTest extends BaseTestSupport {
             Files.delete(remote2);
 
             CommonTestSupportUtils.deleteRecursive(remoteDir);
-        } finally {
-            sshd.setFileSystemFactory(fileSystemFactory);   // restore original
         }
     }