-------------------------------------------------- FakeFtpServer Getting Started -------------------------------------------------- FakeFtpServer - Getting Started ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ <> is a "fake" implementation of an FTP server. It provides a high-level abstraction for an FTP Server and is suitable for most testing and simulation scenarios. You define a virtual filesystem (internal, in-memory) containing an arbitrary set of files and directories. These files and directories can (optionally) have associated access permissions. You also configure a set of one or more user accounts that control which users can login to the FTP server, and their home (default) directories. The user account is also used when assigning file and directory ownership for new files. <> processes FTP client requests and responds with reply codes and reply messages consistent with its configured file system and user accounts, including file and directory permissions, if they have been configured. See the {{{./fakeftpserver-features.html}FakeFtpServer Features and Limitations}} page for more information on which features and scenarios are supported. In general the steps for setting up and starting the <<>> are: * Create a new <<>> instance, and optionally set the server control port (use a value of 0 to automatically choose a free port number). * Create and configure a <<>>, and attach to the <<>> instance. * Create and configure one or more <<>> objects and attach to the <<>> instance. [] Here is an example showing configuration and starting of an <> with a single user account and a (simulated) Windows file system, defining a directory containing two files. +------------------------------------------------------------------------------ FakeFtpServer fakeFtpServer = new FakeFtpServer(); fakeFtpServer.addUserAccount(new UserAccount("user", "password", "c:\\data")); FileSystem fileSystem = new WindowsFakeFileSystem(); fileSystem.add(new DirectoryEntry("c:\\data")); fileSystem.add(new FileEntry("c:\\data\\file1.txt", "abcdef 1234567890")); fileSystem.add(new FileEntry("c:\\data\\run.exe")); fakeFtpServer.setFileSystem(fileSystem); fakeFtpServer.start(); +------------------------------------------------------------------------------ If you are running on a system where the default port (21) is already in use or cannot be bound from a user process (such as Unix), you probably need to use a different server control port. Use the <<>> method to use a different port number. If you specify a value of <<<0>>>, then the server will use a free port number. Then call <<>> AFTER calling <<>> has been called to determine the actual port number being used. Or, you can pass in a specific port number, such as 9187. <> can be fully configured programmatically or within the {{{http://www.springframework.org/}Spring Framework}} or other dependency-injection container. The {{{#Example}Example Test Using FakeFtpServer}} below illustrates programmatic configuration of <<>>. Alternatively, the {{{#Spring}Configuration}} section later on illustrates how to use the to configure a <<>> instance. * {Example} Test Using FakeFtpServer ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This section includes a simplified example of FTP client code to be tested, and a JUnit test for it that programmatically configures and uses <>. ** FTP Client Code ~~~~~~~~~~~~~~~~~~ The following <<>> class includes a <<>> method that retrieves a remote ASCII file and returns its contents as a String. This class uses the <<>> from the {{{http://commons.apache.org/net/}Apache Commons Net}} framework. +------------------------------------------------------------------------------ public class RemoteFile { public static final String USERNAME = "user"; public static final String PASSWORD = "password"; private String server; private int port; public String readFile(String filename) throws IOException { FTPClient ftpClient = new FTPClient(); ftpClient.connect(server, port); ftpClient.login(USERNAME, PASSWORD); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); boolean success = ftpClient.retrieveFile(filename, outputStream); ftpClient.disconnect(); if (!success) { throw new IOException("Retrieve file failed: " + filename); } return outputStream.toString(); } public void setServer(String server) { this.server = server; } public void setPort(int port) { this.port = port; } // Other methods ... } +------------------------------------------------------------------------------ ** JUnit Test For FTP Client Code Using FakeFtpServer ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The following <<>> class includes a couple of JUnit tests that use <>. +------------------------------------------------------------------------------ import org.mockftpserver.fake.filesystem.FileEntry; import org.mockftpserver.fake.filesystem.FileSystem; import org.mockftpserver.fake.filesystem.UnixFakeFileSystem; import org.mockftpserver.fake.FakeFtpServer; import org.mockftpserver.fake.UserAccount; import org.mockftpserver.stub.example.RemoteFile; import org.mockftpserver.test.AbstractTest; import java.io.IOException; import java.util.List; public class RemoteFileTest extends AbstractTest { private static final String HOME_DIR = "/"; private static final String FILE = "/dir/sample.txt"; private static final String CONTENTS = "abcdef 1234567890"; private RemoteFile remoteFile; private FakeFtpServer fakeFtpServer; public void testReadFile() throws Exception { String contents = remoteFile.readFile(FILE); assertEquals("contents", CONTENTS, contents); } public void testReadFileThrowsException() { try { remoteFile.readFile("NoSuchFile.txt"); fail("Expected IOException"); } catch (IOException expected) { // Expected this } } protected void setUp() throws Exception { super.setUp(); fakeFtpServer = new FakeFtpServer(); fakeFtpServer.setServerControlPort(0); // use any free port FileSystem fileSystem = new UnixFakeFileSystem(); fileSystem.add(new FileEntry(FILE, CONTENTS)); fakeFtpServer.setFileSystem(fileSystem); UserAccount userAccount = new UserAccount(RemoteFile.USERNAME, RemoteFile.PASSWORD, HOME_DIR); fakeFtpServer.addUserAccount(userAccount); fakeFtpServer.start(); int port = fakeFtpServer.getServerControlPort(); remoteFile = new RemoteFile(); remoteFile.setServer("localhost"); remoteFile.setPort(port); } protected void tearDown() throws Exception { super.tearDown(); fakeFtpServer.stop(); } } +------------------------------------------------------------------------------ Things to note about the above test: * The <<>> instance is created and started in the <<>> method and stopped in the <<>> method, to ensure that it is stopped, even if the test fails. * The server control port is set to 0 using <<>>. This means it will dynamically choose a free port. This is necessary if you are running on a system where the default port (21) is already in use or cannot be bound from a user process (such as Unix). * The <<>> filesystem is configured and attached to the <<>> instance in the <<>> method. That includes creating a predefined <<<"/dir/sample.txt">>> file with the specified file contents. The <<>> has a <<>> attribute, which defaults to <<>>, meaning that parent directories will be created automatically, as necessary. In this case, that means that the <<<"/">>> and <<<"/dir">>> parent directories will be created, even though not explicitly specified. * A single <<>> with the specified username, password and home directory is configured and attached to the <<>> instance in the <<>> method. That configured user ("user") is the only one that will be able to sucessfully log in to the <<>>. * {Spring} Configuration ~~~~~~~~~~~~~~~~~~~~~~~~ You can easily configure a <<>> instance in the {{{http://www.springframework.org/}Spring Framework}} or another, similar dependency-injection container. ** Simple Spring Configuration Example ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The following example shows a configuration file for a simple <<>> instance. +------------------------------------------------------------------------------ +------------------------------------------------------------------------------ Things to note about the above example: * The <<>> instance has a single user account for username "joe", password "password" and home (default) directory of "/". * A <<>> instance is configured with a predefined directory of "/" and a "/File.txt" file with the specified contents. [] And here is the Java code to load the above configuration file and start the configured <>. +------------------------------------------------------------------------------ ApplicationContext context = new ClassPathXmlApplicationContext("fakeftpserver-beans.xml"); FakeFtpServer = (FakeFtpServer) context.getBean("FakeFtpServer"); FakeFtpServer.start(); +------------------------------------------------------------------------------ ** Spring Configuration Example With File and Directory Permissions ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The following example shows a configuration file for a <<>> instance that also configures file and directory permissions. This will enable the <<>> to reply with proper error codes when the logged in user does not have the required permissions to access directories or files. +------------------------------------------------------------------------------ beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd"> +------------------------------------------------------------------------------ Things to note about the above example: * The <<>> instance is configured with a <<>> and a "c:\" root directory containing two files. Permissions and owner/group are specified for that directory, as well as the two predefined files contained within it. * The permissions for "File1.txt" ("rwxrwxrwx") are specified using the "permissionsFromString" shortcut method, while the permissions for "File2.txt" ("rwx------") are specified using the "permissions" setter, which takes an instance of the <<>> class. Either method is fine. [] * Configuring Custom CommandHandlers ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ <> is intentionally designed to keep the lower-level details of FTP server implementation hidden from the user. In most cases, you can simply define the files and directories in the file system, configure one or more login users, and then fire up the server, expecting it to behave like a FTP server. There are some cases, however, where you might want to further customize the internal behavior of the server. Such cases might include: * You want to have a particular FTP server command return a predetermined error reply * You want to add support for a command that is not provided out of the box by <> Note that if you need the FTP server to reply with entirely predetermined (canned) responses, then you may want to consider using <> instead. ** Using a StaticReplyCommandHandler ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ You can use one of the classes defined within the <<>> package to configure a custom . The following example uses the <<>> from that package to add support for the FEAT command. Note that in this case, we are setting the for a new command (i.e., one that is not supported out of the box by <>). We could just as easily set the for an existing command, overriding the default . +------------------------------------------------------------------------------ import org.mockftpserver.core.command.StaticReplyCommandHandler FakeFtpServer ftpServer = new FakeFtpServer() // ... set up files, directories and user accounts as usual StaticReplyCommandHandler featCommandHandler = new StaticReplyCommandHandler(211, "No Features"); ftpServer.setCommandHandler("FEAT", featCommandHandler); // ... ftpServer.start() +------------------------------------------------------------------------------ ** Using a Stub CommandHandler ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ You can also use a <> -- i.e., one defined within the <<>> package. The following example uses the version of the <<>> from that package. +------------------------------------------------------------------------------ import org.mockftpserver.stub.command.CwdCommandHandler FakeFtpServer ftpServer = new FakeFtpServer() // ... set up files, directories and user accounts as usual final int REPLY_CODE = 502; CwdCommandHandler cwdCommandHandler = new CwdCommandHandler(); cwdCommandHandler.setReplyCode(REPLY_CODE); ftpServer.setCommandHandler(CommandNames.CWD, cwdCommandHandler); // ... ftpServer.start() +------------------------------------------------------------------------------ ** Creating Your Own Custom CommandHandler Class ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ If one of the existing classes does not fulfill your needs, you can alternately create your own custom class. The only requirement is that it implement the <<>> interface. You would, however, likely benefit from inheriting from one of the existing abstract superclasses, such as <<>> or <<>>. See the javadoc of these classes for more information. * FTP Command Reply Text ResourceBundle ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The default text asociated with each FTP command reply code is contained within the "ReplyText.properties" ResourceBundle file. You can customize these messages by providing a locale-specific ResourceBundle file on the CLASSPATH, according to the normal lookup rules of the ResourceBundle class (e.g., "ReplyText_de.properties"). Alternatively, you can completely replace the ResourceBundle file by calling the calling the <<>> method. * SLF4J Configuration Required to See Log Output ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Note that <> uses {{{http://www.slf4j.org/}SLF4J}} for logging. If you want to see the logging output, then you must configure <>. (If no binding is found on the class path, then <> will default to a no-operation implementation.) See the {{{http://www.slf4j.org/manual.html}SLF4J User Manual}} for more information.