Repository: mina-sshd
Updated Branches:
  refs/heads/master 82791d41b -> a6e2bf9e4


http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/a6e2bf9e/sshd-core/src/test/java/org/apache/sshd/client/subsystem/sftp/SftpTest.java
----------------------------------------------------------------------
diff --git 
a/sshd-core/src/test/java/org/apache/sshd/client/subsystem/sftp/SftpTest.java 
b/sshd-core/src/test/java/org/apache/sshd/client/subsystem/sftp/SftpTest.java
new file mode 100644
index 0000000..053aad9
--- /dev/null
+++ 
b/sshd-core/src/test/java/org/apache/sshd/client/subsystem/sftp/SftpTest.java
@@ -0,0 +1,652 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.client.subsystem.sftp;
+
+import static 
org.apache.sshd.common.subsystem.sftp.SftpConstants.SSH_FX_FILE_ALREADY_EXISTS;
+import static 
org.apache.sshd.common.subsystem.sftp.SftpConstants.SSH_FX_NO_SUCH_FILE;
+import static org.apache.sshd.common.subsystem.sftp.SftpConstants.S_IRUSR;
+import static org.apache.sshd.common.subsystem.sftp.SftpConstants.S_IWUSR;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URI;
+import java.nio.file.FileSystem;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.Random;
+import java.util.Vector;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.sshd.client.SshClient;
+import org.apache.sshd.client.session.ClientSession;
+import org.apache.sshd.client.subsystem.sftp.SftpClient;
+import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.file.FileSystemFactory;
+import org.apache.sshd.common.file.root.RootedFileSystemProvider;
+import org.apache.sshd.common.session.Session;
+import org.apache.sshd.common.subsystem.sftp.SftpConstants;
+import org.apache.sshd.common.util.OsUtils;
+import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
+import org.apache.sshd.common.util.io.IoUtils;
+import org.apache.sshd.server.Command;
+import org.apache.sshd.server.SshServer;
+import org.apache.sshd.server.command.ScpCommandFactory;
+import org.apache.sshd.server.subsystem.sftp.SftpSubsystemFactory;
+import org.apache.sshd.util.BaseTestSupport;
+import org.apache.sshd.util.BogusPasswordAuthenticator;
+import org.apache.sshd.util.EchoShellFactory;
+import org.apache.sshd.util.JSchLogger;
+import org.apache.sshd.util.SimpleUserInfo;
+import org.apache.sshd.util.Utils;
+import org.junit.After;
+import org.junit.Assume;
+import org.junit.Before;
+import org.junit.FixMethodOrder;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runners.MethodSorters;
+
+import com.jcraft.jsch.ChannelSftp;
+import com.jcraft.jsch.JSch;
+import com.jcraft.jsch.SftpException;
+
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class SftpTest extends BaseTestSupport {
+
+    private SshServer sshd;
+    private int port;
+    private com.jcraft.jsch.Session session;
+    private final FileSystemFactory fileSystemFactory;
+
+    public SftpTest() throws IOException {
+        Path targetPath = detectTargetFolder().toPath();
+        Path parentPath = targetPath.getParent();
+        final FileSystem fileSystem = new 
RootedFileSystemProvider().newFileSystem(parentPath, 
Collections.<String,Object>emptyMap());
+        fileSystemFactory = new FileSystemFactory() {
+            @Override
+            public FileSystem createFileSystem(Session session) throws 
IOException {
+                return fileSystem;
+            }
+        };
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        sshd = SshServer.setUpDefaultServer();
+        sshd.setKeyPairProvider(Utils.createTestHostKeyProvider());
+        sshd.setSubsystemFactories(Arrays.<NamedFactory<Command>>asList(new 
SftpSubsystemFactory()));
+        sshd.setCommandFactory(new ScpCommandFactory());
+        sshd.setShellFactory(new EchoShellFactory());
+        sshd.setPasswordAuthenticator(BogusPasswordAuthenticator.INSTANCE);
+        sshd.setFileSystemFactory(fileSystemFactory);
+        sshd.start();
+        port = sshd.getPort();
+
+        JSchLogger.init();
+        JSch sch = new JSch();
+        session = sch.getSession("sshd", "localhost", port);
+        session.setUserInfo(new SimpleUserInfo("sshd"));
+        session.connect();
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        if (session != null) {
+            session.disconnect();
+        }
+        
+        if (sshd != null) {
+            sshd.stop(true);
+        }
+    }
+
+    @Test
+    @Ignore
+    public void testExternal() throws Exception {
+        System.out.println("SFTP subsystem available on port " + port);
+        Thread.sleep(5 * 60000);
+    }
+
+    @Test
+    public void testOpen() throws Exception {
+        try(SshClient client = SshClient.setUpDefaultClient()) {
+            client.start();
+            
+            try (ClientSession session = client.connect(getCurrentTestName(), 
"localhost", port).verify(7L, TimeUnit.SECONDS).getSession()) {
+                session.addPasswordIdentity(getCurrentTestName());
+                session.auth().verify(5L, TimeUnit.SECONDS);
+
+                Path targetPath = detectTargetFolder().toPath();
+                Path parentPath = targetPath.getParent();
+                Path lclSftp = Utils.resolve(targetPath, 
SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), 
getCurrentTestName());
+                Path clientFolder = lclSftp.resolve("client");
+                Path testFile = clientFolder.resolve(getCurrentTestName() + 
".txt");
+                String file = Utils.resolveRelativeRemotePath(parentPath, 
testFile);
+
+                File javaFile = testFile.toFile();
+                assertHierarchyTargetFolderExists(javaFile.getParentFile());
+                javaFile.createNewFile();
+                javaFile.setWritable(false, false);
+                javaFile.setReadable(false, false);
+        
+                try (SftpClient sftp = session.createSftpClient()) {
+                    boolean    isWindows = OsUtils.isWin32();
+            
+                    try(SftpClient.CloseableHandle h = sftp.open(file, 
EnumSet.of(SftpClient.OpenMode.Read))) {
+                        // NOTE: on Windows files are always readable
+                        // see 
https://svn.apache.org/repos/asf/harmony/enhanced/java/branches/java6/classlib/modules/luni/src/test/api/windows/org/apache/harmony/luni/tests/java/io/WinFileTest.java
+                        assertTrue("Empty read should have failed on " + file, 
isWindows);
+                    } catch (IOException e) {
+                        if (isWindows) {
+                            throw e;
+                        }
+                    }
+            
+                    try(SftpClient.CloseableHandle h = sftp.open(file, 
EnumSet.of(SftpClient.OpenMode.Write))) {
+                        fail("Empty write should have failed on " + file);
+                    } catch (IOException e) {
+                        // ok
+                    }
+    
+                    try(SftpClient.CloseableHandle h = sftp.open(file, 
EnumSet.of(SftpClient.OpenMode.Truncate))) {
+                        // NOTE: on Windows files are always readable
+                        assertTrue("Empty truncate should have failed on " + 
file, isWindows);
+                    } catch (IOException e) {
+                        // ok
+                    }
+    
+                    // NOTE: on Windows files are always readable
+                    int        perms=sftp.stat(file).perms;
+                    int        permsMask=S_IWUSR | (isWindows ? 0 : S_IRUSR);
+                    assertEquals("Mismatched permissions for " + file + ": 0x" 
+ Integer.toHexString(perms), 0, (perms & permsMask));
+            
+                    javaFile.setWritable(true, false);
+            
+                    try(SftpClient.CloseableHandle h = sftp.open(file, 
EnumSet.of(SftpClient.OpenMode.Truncate, SftpClient.OpenMode.Write))) {
+                        // OK should succeed
+                        assertTrue("Handle not marked as open for file=" + 
file, h.isOpen());
+                    }
+            
+                    byte[] d = "0123456789\n".getBytes();
+                    try(SftpClient.CloseableHandle h = sftp.open(file, 
EnumSet.of(SftpClient.OpenMode.Write))) {
+                        sftp.write(h, 0, d, 0, d.length);
+                        sftp.write(h, d.length, d, 0, d.length);
+                    }
+
+                    try(SftpClient.CloseableHandle h = sftp.open(file, 
EnumSet.of(SftpClient.OpenMode.Write))) {
+                        sftp.write(h, d.length * 2, d, 0, d.length);
+                    }
+
+                    try(SftpClient.CloseableHandle h = sftp.open(file, 
EnumSet.of(SftpClient.OpenMode.Write))) {
+                        sftp.write(h, 3, "-".getBytes(), 0, 1);
+                    }
+
+                    try(SftpClient.CloseableHandle h = sftp.open(file, 
EnumSet.of(SftpClient.OpenMode.Read))) {
+                        // NOTE: on Windows files are always readable
+                        assertTrue("Data read should have failed on " + file, 
isWindows);
+                    } catch (IOException e) {
+                        if (isWindows) {
+                            throw e;
+                        }
+                    }
+            
+                    javaFile.setReadable(true, false);
+            
+                    byte[] buf = new byte[3];
+                    try(SftpClient.CloseableHandle h = sftp.open(file, 
EnumSet.of(SftpClient.OpenMode.Read))) {
+                        int l = sftp.read(h, 2l, buf, 0, 3);
+                        assertEquals("Mismatched read data", "2-4", new 
String(buf, 0, l));
+                    }
+                }
+            } finally {
+                client.stop();
+            }
+        }
+    }
+
+    @Test
+    public void testClient() throws Exception {
+        try(SshClient client = SshClient.setUpDefaultClient()) {
+            client.start();
+
+            try (ClientSession session = client.connect(getCurrentTestName(), 
"localhost", port).verify(7L, TimeUnit.SECONDS).getSession()) {
+                session.addPasswordIdentity(getCurrentTestName());
+                session.auth().verify(5L, TimeUnit.SECONDS);
+
+                Path targetPath = detectTargetFolder().toPath();
+                Path lclSftp = Utils.resolve(targetPath, 
SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), 
getCurrentTestName());
+                Utils.deleteRecursive(lclSftp);
+                Files.createDirectories(lclSftp);
+
+                Path parentPath = targetPath.getParent();
+                Path clientFolder = lclSftp.resolve("client");
+                String dir = Utils.resolveRelativeRemotePath(parentPath, 
clientFolder);
+                String file = dir + "/" + getCurrentTestName() + ".txt";
+
+                try (SftpClient sftp = session.createSftpClient()) {
+                    sftp.mkdir(dir);
+            
+                    try(SftpClient.CloseableHandle h = sftp.open(file, 
EnumSet.of(SftpClient.OpenMode.Write, SftpClient.OpenMode.Create))) {
+                        byte[] d = "0123456789\n".getBytes();
+                        sftp.write(h, 0, d, 0, d.length);
+                        sftp.write(h, d.length, d, 0, d.length);
+                
+                        SftpClient.Attributes attrs = sftp.stat(h);
+                        assertNotNull("No handle attributes", attrs);
+                    }            
+            
+                    try(SftpClient.CloseableHandle h = sftp.openDir(dir)) {
+                        SftpClient.DirEntry[] dirEntries = sftp.readDir(h);
+                        assertNotNull("No dir entries", dirEntries);
+                        assertEquals("Mismatced number of dir entries", 1, 
dirEntries.length);
+                        assertNull("Unexpected entry read", sftp.readDir(h));
+                    }
+            
+                    sftp.remove(file);
+    
+                    byte[] workBuf = new byte[IoUtils.DEFAULT_COPY_SIZE * 
Short.SIZE];
+                    new Random(System.currentTimeMillis()).nextBytes(workBuf);
+                    try (OutputStream os = sftp.write(file)) {
+                        os.write(workBuf);
+                    }
+            
+                    try (InputStream is = sftp.read(file, 
IoUtils.DEFAULT_COPY_SIZE)) {
+                        int readLen = is.read(workBuf);
+                        assertEquals("Mismatched read data length", 
workBuf.length, readLen);
+        
+                        int i = is.read();
+                        assertEquals("Unexpected read past EOF", -1, i);
+                    }
+        
+                    SftpClient.Attributes attributes = sftp.stat(file);
+                    assertTrue("Test file not detected as regular", 
attributes.isRegularFile());
+            
+                    attributes = sftp.stat(dir);
+                    assertTrue("Test directory not reported as such", 
attributes.isDirectory());
+            
+                    int nb = 0;
+                    for (SftpClient.DirEntry entry : sftp.readDir(dir)) {
+                        assertNotNull("Unexpected null entry", entry);
+                        nb++;
+                    }
+                    assertEquals("Mismatched read dir entries", 1, nb);
+            
+                    sftp.remove(file);
+            
+                    sftp.rmdir(dir);
+                }
+            } finally {
+                client.stop();
+            }
+        }
+    }
+
+    /**
+     * this test is meant to test out write's logic, to ensure that internal 
chunking (based on Buffer.MAX_LEN) is
+     * functioning properly. To do this, we write a variety of file sizes, 
both smaller and larger than Buffer.MAX_LEN.
+     * in addition, this test ensures that improper arguments passed in get 
caught with an IllegalArgumentException
+     * @throws Exception upon any uncaught exception or failure
+     */
+    @Test
+    public void testWriteChunking() throws Exception {
+        try(SshClient client = SshClient.setUpDefaultClient()) {
+            client.start();
+            
+            try (ClientSession session = client.connect(getCurrentTestName(), 
"localhost", port).verify(7L, TimeUnit.SECONDS).getSession()) {
+                session.addPasswordIdentity(getCurrentTestName());
+                session.auth().verify(5L, TimeUnit.SECONDS);
+        
+                Path targetPath = detectTargetFolder().toPath();
+                Path lclSftp = Utils.resolve(targetPath, 
SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), 
getCurrentTestName());
+                Utils.deleteRecursive(lclSftp);
+                Files.createDirectories(lclSftp);
+
+                Path parentPath = targetPath.getParent();
+                Path clientFolder = lclSftp.resolve("client");
+                String dir = Utils.resolveRelativeRemotePath(parentPath, 
clientFolder);
+        
+                try(SftpClient sftp = session.createSftpClient()) {
+                    sftp.mkdir(dir);
+            
+                    uploadAndVerifyFile(sftp, clientFolder, dir, 0, 
"emptyFile.txt");
+                    uploadAndVerifyFile(sftp, clientFolder, dir, 1000, 
"smallFile.txt");
+                    uploadAndVerifyFile(sftp, clientFolder, dir, 
ByteArrayBuffer.MAX_LEN - 1, "bufferMaxLenMinusOneFile.txt");
+                    uploadAndVerifyFile(sftp, clientFolder, dir, 
ByteArrayBuffer.MAX_LEN, "bufferMaxLenFile.txt");
+                    // were chunking not implemented, these would fail. these 
sizes should invoke our internal chunking mechanism
+                    uploadAndVerifyFile(sftp, clientFolder, dir, 
ByteArrayBuffer.MAX_LEN + 1, "bufferMaxLenPlusOneFile.txt");
+                    uploadAndVerifyFile(sftp, clientFolder, dir, (int)(1.5 * 
ByteArrayBuffer.MAX_LEN), "1point5BufferMaxLenFile.txt");
+                    uploadAndVerifyFile(sftp, clientFolder, dir, (2 * 
ByteArrayBuffer.MAX_LEN) - 1, "2TimesBufferMaxLenMinusOneFile.txt");
+                    uploadAndVerifyFile(sftp, clientFolder, dir, 2 * 
ByteArrayBuffer.MAX_LEN, "2TimesBufferMaxLenFile.txt");
+                    uploadAndVerifyFile(sftp, clientFolder, dir, (2 * 
ByteArrayBuffer.MAX_LEN) + 1, "2TimesBufferMaxLenPlusOneFile.txt");
+                    uploadAndVerifyFile(sftp, clientFolder, dir, 200000, 
"largerFile.txt");
+            
+                    // test erroneous calls that check for negative values
+                    Path invalidPath = 
clientFolder.resolve(getCurrentTestName() + "-invalid");
+                    testInvalidParams(sftp, invalidPath, 
Utils.resolveRelativeRemotePath(parentPath, invalidPath));
+            
+                    // cleanup
+                    sftp.rmdir(dir);
+                }
+            } finally {
+                client.stop();
+            }
+        }
+    }
+
+    private void testInvalidParams(SftpClient sftp, Path file, String 
filePath) throws Exception {
+        // generate random file and upload it
+        String randomData = randomString(5);
+        byte[] randomBytes = randomData.getBytes();
+        try(SftpClient.CloseableHandle handle = sftp.open(filePath, 
EnumSet.of(SftpClient.OpenMode.Write, SftpClient.OpenMode.Create))) {
+            try {
+                sftp.write(handle, -1, randomBytes, 0, 0);
+                fail("should not have been able to write file with invalid 
file offset for " + filePath);
+            } catch (IllegalArgumentException e) {
+                // expected
+            }
+            try {
+                sftp.write(handle, 0, randomBytes, -1, 0);
+                fail("should not have been able to write file with invalid 
source offset for " + filePath);
+            } catch (IllegalArgumentException e) {
+                // expected
+            }
+            try {
+                sftp.write(handle, 0, randomBytes, 0, -1);
+                fail("should not have been able to write file with invalid 
length for " + filePath);
+            } catch (IllegalArgumentException e) {
+                // expected
+            }
+            try {
+                sftp.write(handle, 0, randomBytes, 0, randomBytes.length + 1);
+                fail("should not have been able to write file with length 
bigger than array itself (no offset) for " + filePath);
+            } catch (IllegalArgumentException e) {
+                // expected
+            }
+            try {
+                sftp.write(handle, 0, randomBytes, randomBytes.length, 1);
+                fail("should not have been able to write file with length 
bigger than array itself (with offset) for " + filePath);
+            } catch (IllegalArgumentException e) {
+                // expected
+            }
+        }
+
+        sftp.remove(filePath);
+        assertFalse("File should not be there: " + file.toString(), 
Files.exists(file));
+    }
+
+    private void uploadAndVerifyFile(SftpClient sftp, Path clientFolder, 
String remoteDir, int size, String filename) throws Exception {
+        // generate random file and upload it
+        String remotePath = remoteDir + "/" + filename;
+        String randomData = randomString(size);
+        try(SftpClient.CloseableHandle handle = sftp.open(remotePath, 
EnumSet.of(SftpClient.OpenMode.Write, SftpClient.OpenMode.Create))) {
+            sftp.write(handle, 0, randomData.getBytes(), 0, 
randomData.length());
+        }
+
+        // verify results
+        Path resultPath = clientFolder.resolve(filename);
+        assertTrue("File should exist on disk: " + resultPath, 
Files.exists(resultPath));
+        assertTrue("Mismatched file contents: " + resultPath, 
randomData.equals(readFile(remotePath)));
+
+        // cleanup
+        sftp.remove(remotePath);
+        assertFalse("File should have been removed: " + resultPath, 
Files.exists(resultPath));
+    }
+
+    @Test
+    public void testSftp() throws Exception {
+        String d = getCurrentTestName() + "\n";
+
+        Path targetPath = detectTargetFolder().toPath();
+        Path lclSftp = Utils.resolve(targetPath, 
SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), 
getCurrentTestName());
+        Utils.deleteRecursive(lclSftp);
+        Files.createDirectories(lclSftp);
+
+        Path target = lclSftp.resolve(getCurrentTestName() + ".txt");
+        String remotePath = 
Utils.resolveRelativeRemotePath(targetPath.getParent(), target);
+
+        final int NUM_ITERATIONS=10;
+        StringBuilder   sb = new StringBuilder(d.length() * NUM_ITERATIONS * 
NUM_ITERATIONS);
+        for (int j = 1; j <= NUM_ITERATIONS; j++) {
+            if (sb.length() > 0) {
+                sb.setLength(0);
+            }
+
+            for (int i = 0; i < j; i++) {
+                sb.append(d);
+            }
+
+            sendFile(remotePath, sb.toString());
+            assertFileLength(target, sb.length(), 5000);
+            Files.delete(target);
+        }
+    }
+
+    @Test
+    public void testReadWriteWithOffset() throws Exception {
+        Path targetPath = detectTargetFolder().toPath();
+        Path lclSftp = Utils.resolve(targetPath, 
SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), 
getCurrentTestName());
+        Utils.deleteRecursive(lclSftp);
+        Files.createDirectories(lclSftp);
+
+        Path localPath = lclSftp.resolve(getCurrentTestName() + ".txt");
+        String remotePath = 
Utils.resolveRelativeRemotePath(targetPath.getParent(), localPath);
+        String data = getCurrentTestName();
+        String extraData = "@" + getClass().getSimpleName();
+        int appendOffset = -5;
+
+        ChannelSftp c = (ChannelSftp) 
session.openChannel(SftpConstants.SFTP_SUBSYSTEM_NAME);
+        c.connect();
+        try {
+            c.put(new ByteArrayInputStream(data.getBytes()), remotePath);
+    
+            assertTrue("Remote file not created after initial write: " + 
localPath, Files.exists(localPath));
+            assertEquals("Mismatched data read from " + remotePath, data, 
readFile(remotePath));
+    
+            try(OutputStream os = c.put(remotePath, null, ChannelSftp.APPEND, 
appendOffset)) {
+                os.write(extraData.getBytes());
+            }
+        } finally {
+            c.disconnect();
+        }
+
+        assertTrue("Remote file not created after data update: " + localPath, 
Files.exists(localPath));
+        
+        String expected = data.substring(0, data.length() + appendOffset) + 
extraData;
+        String actual = readFile(remotePath);
+        assertEquals("Mismatched final file data in " + remotePath, expected, 
actual);
+    }
+
+    @Test
+    public void testReadDir() throws Exception {
+        ChannelSftp c = (ChannelSftp) 
session.openChannel(SftpConstants.SFTP_SUBSYSTEM_NAME);
+        c.connect();
+        try {
+            URI url = 
getClass().getClassLoader().getResource(SshClient.class.getName().replace('.', 
'/') + ".class").toURI();
+            URI base = new 
File(System.getProperty("user.dir")).getAbsoluteFile().toURI();
+            String path = new File(base.relativize(url).getPath()).getParent() 
+ "/";
+            path = path.replace('\\', '/');
+            Vector<?> res = c.ls(path);
+            for (Object f : res) {
+                System.out.println(f.toString());
+            }
+        } finally {
+            c.disconnect();
+        }
+    }
+
+    @Test
+    public void testRealPath() throws Exception {
+        ChannelSftp c = (ChannelSftp) 
session.openChannel(SftpConstants.SFTP_SUBSYSTEM_NAME);
+        c.connect();
+
+        try {
+            URI url = 
getClass().getClassLoader().getResource(SshClient.class.getName().replace('.', 
'/') + ".class").toURI();
+            URI base = new 
File(System.getProperty("user.dir")).getAbsoluteFile().toURI();
+            String path = new File(base.relativize(url).getPath()).getParent() 
+ "/";
+            path = path.replace('\\', '/');
+            String real = c.realpath(path);
+            System.out.println(real);
+            try {
+                real = c.realpath(path + "/foobar");
+                System.out.println(real);
+                fail("Expected SftpException");
+            } catch (SftpException e) {
+                // ok
+            }
+        } finally {
+            c.disconnect();
+        }
+    }
+
+    @Test
+    public void testRename() throws Exception {
+        try(SshClient client = SshClient.setUpDefaultClient()) {
+            client.start();
+            
+            try (ClientSession session = client.connect(getCurrentTestName(), 
"localhost", port).verify(7L, TimeUnit.SECONDS).getSession()) {
+                session.addPasswordIdentity(getCurrentTestName());
+                session.auth().verify(5L, TimeUnit.SECONDS);
+        
+                Path targetPath = detectTargetFolder().toPath();
+                Path lclSftp = Utils.resolve(targetPath, 
SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), 
getCurrentTestName());
+                Utils.deleteRecursive(lclSftp);
+                Files.createDirectories(lclSftp);
+
+                Path parentPath = targetPath.getParent();
+                Path clientFolder = 
assertHierarchyTargetFolderExists(lclSftp.resolve("client"));
+        
+                try(SftpClient sftp = session.createSftpClient()) {
+                    Path file1 = clientFolder.resolve(getCurrentTestName() + 
"-1.txt");
+                    String file1Path = 
Utils.resolveRelativeRemotePath(parentPath, file1);
+                    try (OutputStream os = sftp.write(file1Path, 
SftpClient.MIN_WRITE_BUFFER_SIZE)) {
+                        os.write((getCurrentTestName() + "\n").getBytes());
+                    }
+
+                    Path file2 = clientFolder.resolve(getCurrentTestName() + 
"-2.txt");
+                    String file2Path = 
Utils.resolveRelativeRemotePath(parentPath, file2);
+                    Path file3 = clientFolder.resolve(getCurrentTestName() + 
"-3.txt");
+                    String file3Path = 
Utils.resolveRelativeRemotePath(parentPath, file3);
+                    try {
+                        sftp.rename(file2Path, file3Path);
+                        fail("Unxpected rename success of " + file2Path + " => 
" + file3Path);
+                    } catch (org.apache.sshd.client.SftpException e) {
+                        assertEquals("Mismatched status for failed rename of " 
+ file2Path + " => " + file3Path, SSH_FX_NO_SUCH_FILE, e.getStatus());
+                    }
+            
+                    try (OutputStream os = sftp.write(file2Path, 
SftpClient.MIN_WRITE_BUFFER_SIZE)) {
+                        os.write("H".getBytes());
+                    }
+            
+                    try {
+                        sftp.rename(file1Path, file2Path);
+                        fail("Unxpected rename success of " + file1Path + " => 
" + file2Path);
+                    } catch (org.apache.sshd.client.SftpException e) {
+                        assertEquals("Mismatched status for failed rename of " 
+ file1Path + " => " + file2Path, SSH_FX_FILE_ALREADY_EXISTS, e.getStatus());
+                    }
+
+                    sftp.rename(file1Path, file2Path, 
SftpClient.CopyMode.Overwrite);
+                }
+            } finally {
+                client.stop();
+            }
+        }
+    }
+
+    @Test
+    public void testCreateSymbolicLink() throws Exception {
+        // Do not execute on windows as the file system does not support 
symlinks
+        Assume.assumeTrue("Skip non-Unix O/S", OsUtils.isUNIX());
+
+        Path targetPath = detectTargetFolder().toPath();
+        Path lclSftp = Utils.resolve(targetPath, 
SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), 
getCurrentTestName());
+        Utils.deleteRecursive(lclSftp);
+        Files.createDirectories(lclSftp);
+
+        Path parentPath = targetPath.getParent();
+        Path sourcePath = lclSftp.resolve(getCurrentTestName() + ".txt");
+        String remSrcPath = Utils.resolveRelativeRemotePath(parentPath, 
sourcePath);
+        Path linkPath = lclSftp.resolve("link-" + sourcePath.getFileName());
+        String remLinkPath = Utils.resolveRelativeRemotePath(parentPath, 
linkPath);
+
+        String data = getCurrentTestName();
+        ChannelSftp c = (ChannelSftp) 
session.openChannel(SftpConstants.SFTP_SUBSYSTEM_NAME);
+        c.connect();
+        try {
+            c.put(new ByteArrayInputStream(data.getBytes()), remSrcPath);
+    
+            assertTrue("Source file not created: " + sourcePath, 
Files.exists(sourcePath));
+            assertEquals("Mismatched stored data in " + remSrcPath, data, 
readFile(remSrcPath));
+    
+            c.symlink(remSrcPath, remLinkPath);
+    
+            assertTrue("Symlink not created: " + linkPath, 
Files.exists(linkPath));
+            assertEquals("Mismatche link data in " + remLinkPath, data, 
readFile(remLinkPath));
+    
+            String str1 = c.readlink(remLinkPath);
+            String str2 = c.realpath(remSrcPath);
+            assertEquals("Mismatched link vs. real path", str1, str2);
+        } finally {
+            c.disconnect();
+        }
+    }
+
+    protected String readFile(String path) throws Exception {
+        ChannelSftp c = (ChannelSftp) 
session.openChannel(SftpConstants.SFTP_SUBSYSTEM_NAME);
+        c.connect();
+        
+        try(ByteArrayOutputStream bos = new ByteArrayOutputStream();
+            InputStream is = c.get(path)) {
+
+            byte[] buffer = new byte[256];
+            int count;
+            while (-1 != (count = is.read(buffer))) {
+                bos.write(buffer, 0, count);
+            }
+
+            return bos.toString();
+        } finally {
+            c.disconnect();
+        }
+    }
+
+    protected void sendFile(String path, String data) throws Exception {
+        ChannelSftp c = (ChannelSftp) 
session.openChannel(SftpConstants.SFTP_SUBSYSTEM_NAME);
+        c.connect();
+        try {
+            c.put(new ByteArrayInputStream(data.getBytes()), path);
+        } finally {
+            c.disconnect();
+        }
+    }
+
+    private String randomString(int size) {
+        StringBuilder sb = new StringBuilder(size);
+        for (int i = 0; i < size; i++) {
+            sb.append((char) ((i % 10) + '0'));
+        }
+        return sb.toString();
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/a6e2bf9e/sshd-core/src/test/java/org/apache/sshd/server/ServerTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/server/ServerTest.java 
b/sshd-core/src/test/java/org/apache/sshd/server/ServerTest.java
index f9d6e17..43af998 100644
--- a/sshd-core/src/test/java/org/apache/sshd/server/ServerTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/server/ServerTest.java
@@ -54,7 +54,7 @@ import org.apache.sshd.common.session.Session;
 import org.apache.sshd.common.session.SessionListener;
 import org.apache.sshd.deprecated.ClientUserAuthServiceOld;
 import org.apache.sshd.server.command.ScpCommandFactory;
-import org.apache.sshd.server.sftp.SftpSubsystemFactory;
+import org.apache.sshd.server.subsystem.sftp.SftpSubsystemFactory;
 import org.apache.sshd.util.BaseTestSupport;
 import org.apache.sshd.util.BogusPasswordAuthenticator;
 import org.apache.sshd.util.EchoShellFactory;

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/a6e2bf9e/sshd-core/src/test/java/org/apache/sshd/server/sftp/SftpSubsystemFactoryTest.java
----------------------------------------------------------------------
diff --git 
a/sshd-core/src/test/java/org/apache/sshd/server/sftp/SftpSubsystemFactoryTest.java
 
b/sshd-core/src/test/java/org/apache/sshd/server/sftp/SftpSubsystemFactoryTest.java
deleted file mode 100644
index 8807fae..0000000
--- 
a/sshd-core/src/test/java/org/apache/sshd/server/sftp/SftpSubsystemFactoryTest.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.sshd.server.sftp;
-
-import java.util.concurrent.ExecutorService;
-
-import org.apache.sshd.util.BaseTestSupport;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.runners.MethodSorters;
-import org.mockito.Mockito;
-
-/**
- * @author <a href="mailto:[email protected]";>Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-public class SftpSubsystemFactoryTest extends BaseTestSupport {
-    public SftpSubsystemFactoryTest() {
-        super();
-    }
-
-    /**
-     * Make sure that the builder returns a factory with the default values
-     * if no {@code withXXX} method is invoked
-     */
-    @Test
-    public void testBuilderDefaultFactoryValues() {
-        SftpSubsystemFactory factory = new 
SftpSubsystemFactory.Builder().build();
-        assertNull("Mismatched executor", factory.getExecutorService());
-        assertFalse("Mismatched shutdown state", factory.isShutdownOnExit());
-        assertSame("Mismatched unsupported attribute policy", 
SftpSubsystemFactory.DEFAULT_POLICY, factory.getUnsupportedAttributePolicy());
-    }
-
-    /**
-     * Make sure that the builder initializes correctly the built factory
-     */
-    @Test
-    public void testBuilderCorrectlyInitializesFactory() {
-        SftpSubsystemFactory.Builder builder = new 
SftpSubsystemFactory.Builder();
-        ExecutorService service = dummyExecutor();
-        SftpSubsystemFactory factory = builder.withExecutorService(service)
-                .withShutdownOnExit(true)
-                .build();
-        assertSame("Mismatched executor", service, 
factory.getExecutorService());
-        assertTrue("Mismatched shutdown state", factory.isShutdownOnExit());
-
-        for (UnsupportedAttributePolicy policy : 
UnsupportedAttributePolicy.VALUES) {
-            SftpSubsystemFactory actual = 
builder.withUnsupportedAttributePolicy(policy).build();
-            assertSame("Mismatched unsupported attribute policy", policy, 
actual.getUnsupportedAttributePolicy());
-        }
-    }
-
-    /**
-     * <UL>
-     * <LI>
-     * Make sure the builder returns new instances on every call to
-     * {@link SftpSubsystemFactory.Builder#build()} method
-     * </LI>
-     * <p/>
-     * <LI>
-     * Make sure values are preserved between successive invocations
-     * of the {@link SftpSubsystemFactory.Builder#build()} method
-     * </LI>
-     * </UL
-     */
-    @Test
-    public void testBuilderUniqueInstance() {
-        SftpSubsystemFactory.Builder builder = new 
SftpSubsystemFactory.Builder();
-        SftpSubsystemFactory f1 = 
builder.withExecutorService(dummyExecutor()).build();
-        SftpSubsystemFactory f2 = builder.build();
-        assertNotSame("No new instance built", f1, f2);
-        assertSame("Mismatched executors", f1.getExecutorService(), 
f2.getExecutorService());
-
-        SftpSubsystemFactory f3 = 
builder.withExecutorService(dummyExecutor()).build();
-        assertNotSame("Executor service not changed", f1.getExecutorService(), 
f3.getExecutorService());
-    }
-
-    private static ExecutorService dummyExecutor() {
-        return Mockito.mock(ExecutorService.class);
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/a6e2bf9e/sshd-core/src/test/java/org/apache/sshd/server/subsystem/sftp/SftpSubsystemFactoryTest.java
----------------------------------------------------------------------
diff --git 
a/sshd-core/src/test/java/org/apache/sshd/server/subsystem/sftp/SftpSubsystemFactoryTest.java
 
b/sshd-core/src/test/java/org/apache/sshd/server/subsystem/sftp/SftpSubsystemFactoryTest.java
new file mode 100644
index 0000000..7a251ea
--- /dev/null
+++ 
b/sshd-core/src/test/java/org/apache/sshd/server/subsystem/sftp/SftpSubsystemFactoryTest.java
@@ -0,0 +1,100 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.server.subsystem.sftp;
+
+import java.util.concurrent.ExecutorService;
+
+import org.apache.sshd.server.subsystem.sftp.SftpSubsystemFactory;
+import org.apache.sshd.server.subsystem.sftp.UnsupportedAttributePolicy;
+import org.apache.sshd.util.BaseTestSupport;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runners.MethodSorters;
+import org.mockito.Mockito;
+
+/**
+ * @author <a href="mailto:[email protected]";>Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class SftpSubsystemFactoryTest extends BaseTestSupport {
+    public SftpSubsystemFactoryTest() {
+        super();
+    }
+
+    /**
+     * Make sure that the builder returns a factory with the default values
+     * if no {@code withXXX} method is invoked
+     */
+    @Test
+    public void testBuilderDefaultFactoryValues() {
+        SftpSubsystemFactory factory = new 
SftpSubsystemFactory.Builder().build();
+        assertNull("Mismatched executor", factory.getExecutorService());
+        assertFalse("Mismatched shutdown state", factory.isShutdownOnExit());
+        assertSame("Mismatched unsupported attribute policy", 
SftpSubsystemFactory.DEFAULT_POLICY, factory.getUnsupportedAttributePolicy());
+    }
+
+    /**
+     * Make sure that the builder initializes correctly the built factory
+     */
+    @Test
+    public void testBuilderCorrectlyInitializesFactory() {
+        SftpSubsystemFactory.Builder builder = new 
SftpSubsystemFactory.Builder();
+        ExecutorService service = dummyExecutor();
+        SftpSubsystemFactory factory = builder.withExecutorService(service)
+                .withShutdownOnExit(true)
+                .build();
+        assertSame("Mismatched executor", service, 
factory.getExecutorService());
+        assertTrue("Mismatched shutdown state", factory.isShutdownOnExit());
+
+        for (UnsupportedAttributePolicy policy : 
UnsupportedAttributePolicy.VALUES) {
+            SftpSubsystemFactory actual = 
builder.withUnsupportedAttributePolicy(policy).build();
+            assertSame("Mismatched unsupported attribute policy", policy, 
actual.getUnsupportedAttributePolicy());
+        }
+    }
+
+    /**
+     * <UL>
+     * <LI>
+     * Make sure the builder returns new instances on every call to
+     * {@link SftpSubsystemFactory.Builder#build()} method
+     * </LI>
+     * <p/>
+     * <LI>
+     * Make sure values are preserved between successive invocations
+     * of the {@link SftpSubsystemFactory.Builder#build()} method
+     * </LI>
+     * </UL
+     */
+    @Test
+    public void testBuilderUniqueInstance() {
+        SftpSubsystemFactory.Builder builder = new 
SftpSubsystemFactory.Builder();
+        SftpSubsystemFactory f1 = 
builder.withExecutorService(dummyExecutor()).build();
+        SftpSubsystemFactory f2 = builder.build();
+        assertNotSame("No new instance built", f1, f2);
+        assertSame("Mismatched executors", f1.getExecutorService(), 
f2.getExecutorService());
+
+        SftpSubsystemFactory f3 = 
builder.withExecutorService(dummyExecutor()).build();
+        assertNotSame("Executor service not changed", f1.getExecutorService(), 
f3.getExecutorService());
+    }
+
+    private static ExecutorService dummyExecutor() {
+        return Mockito.mock(ExecutorService.class);
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/a6e2bf9e/sshd-git/src/test/java/org/apache/sshd/git/pack/GitPackCommandTest.java
----------------------------------------------------------------------
diff --git 
a/sshd-git/src/test/java/org/apache/sshd/git/pack/GitPackCommandTest.java 
b/sshd-git/src/test/java/org/apache/sshd/git/pack/GitPackCommandTest.java
index 210e35c..7ba2362 100644
--- a/sshd-git/src/test/java/org/apache/sshd/git/pack/GitPackCommandTest.java
+++ b/sshd-git/src/test/java/org/apache/sshd/git/pack/GitPackCommandTest.java
@@ -28,7 +28,7 @@ import org.apache.sshd.git.util.EchoShellFactory;
 import org.apache.sshd.git.util.Utils;
 import org.apache.sshd.server.Command;
 import org.apache.sshd.server.SshServer;
-import org.apache.sshd.server.sftp.SftpSubsystemFactory;
+import org.apache.sshd.server.subsystem.sftp.SftpSubsystemFactory;
 import org.eclipse.jgit.api.Git;
 import org.eclipse.jgit.transport.CredentialsProvider;
 import org.eclipse.jgit.transport.SshSessionFactory;

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/a6e2bf9e/sshd-git/src/test/java/org/apache/sshd/git/pgm/GitPgmCommandTest.java
----------------------------------------------------------------------
diff --git 
a/sshd-git/src/test/java/org/apache/sshd/git/pgm/GitPgmCommandTest.java 
b/sshd-git/src/test/java/org/apache/sshd/git/pgm/GitPgmCommandTest.java
index ba186fa..b1d45e9 100644
--- a/sshd-git/src/test/java/org/apache/sshd/git/pgm/GitPgmCommandTest.java
+++ b/sshd-git/src/test/java/org/apache/sshd/git/pgm/GitPgmCommandTest.java
@@ -32,7 +32,7 @@ import org.apache.sshd.git.util.EchoShellFactory;
 import org.apache.sshd.git.util.Utils;
 import org.apache.sshd.server.Command;
 import org.apache.sshd.server.SshServer;
-import org.apache.sshd.server.sftp.SftpSubsystemFactory;
+import org.apache.sshd.server.subsystem.sftp.SftpSubsystemFactory;
 import org.eclipse.jgit.api.Git;
 import org.junit.FixMethodOrder;
 import org.junit.Test;

Reply via email to